I'm trying to implement sorting functionality in my notes app but have some problems with it. My sort function must just reverse an array of notes but it does not work.
I have a state with notes:
export const initialState = {
notes: [
{
id: nanoid(),
text: '',
date: '',
},
],
}
Action:
export const sortNoteAction = ([notes]) => ({
type: SORT_NOTE,
payload: [notes],
})
Reducer:
export default function notes(state = initialState, { type, payload }) {
switch (type) {
case SORT_NOTE: {
return {
...state,
notes: [payload].reverse(),
}
}
default:
return state
}
}
Action:
export const sortNoteAction = (notes=[]) => ({
type: SORT_NOTE,
payload: notes,
})
Reducer:
export default function notes(state = initialState, action) {
switch (action.type) {
case SORT_NOTE: {
return {
...state,
notes: action.payload.reverse(),
}
}
default:
return state
}
}
I think it doesn't work because you are essentially sorting a list with just one item. If you change [notes] to notes (2 occurrences) and [payload] to payload (1 occurrence) it will probably work
Related
I build an app in React with Redux and I try to send to my state an object and I try to save it in 'thisUser' but I don't know how to write that 'return' because mine doesn't work.
My Redux state:
const initialState = {
thisUser: {}
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: { ...state.thisUser, ...action.payload} } //the problem
default:
return state
}
}
Dispatch method:
dispatch({ type: "users/addUser", payload: new_user });
Can you tell me how to write that return, please?
If you want to append new user then why are you using object type. You should use Array Type thisUser.
const initialState = {
thisUser: []
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: [ ...state.thisUser,action.payload ] }
default:
return state
}
}
Or
If you want to save only single user object then change only that line in your code:
return { ...state, thisUser: action.payload }
It's better to use an array type for if you have a list of users .
If you have a case when you need to use an object just change the brackets [ ] on my code to curly braces { } .
const initialState = {
thisUser: [],
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: [ ...state.thisUser, ...action.payload]}
default:
return state
}
}
I am working on a React application and I am using Redux to store the state. I have the following the code:
menu.actions.js:
import { apiUrl, apiConfig } from '../../util/api';
import { ADD_CATEGORY, GET_MENU } from './menu.types';
export const getMenu = () => async dispatch => {
const response = await fetch(`${apiUrl}/menu`);
if (response.ok) {
const menuData = await response.json();
dispatch({ type: GET_MENU, payload: menuData })
}
}
export const addCategory = category => async dispatch => {
const options = {
...apiConfig(),
method: 'POST',
body: JSON.stringify(category)
};
const response = await fetch(apiUrl + '/category/', options)
let data = await response.json()
if (response.ok) {
dispatch({ type: ADD_CATEGORY, payload: { ...data } })
} else {
alert(data.error)
}
}
menu.reducer.js:
import { ADD_CATEGORY, GET_MENU } from './menu.types';
const INITIAL_STATE = []
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_MENU:
return [...action.payload];
case ADD_CATEGORY:
return [ ...state, { ...action.payload, menuItem: [] } ]
default:
return state;
}
}
In the above Reducer, the initial state is an empty array. Dispatching the GET_MENU action, changes the initial state so that it contains an array of menu items instead.
The array that is fetched in the GET_MENU action is of the following:
I have modified the code in the Reducer function so that the initial state is now the following:
menu.reducernew.js:
import { ADD_CATEGORY, GET_MENU } from './menu.types';
const INITIAL_STATE = {
menuArray: [],
isSending: false
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_MENU:
return {
...state,
menuArray: action.payload
};
case ADD_CATEGORY:
return {
...state,
menuArray: [ ...menuArray, { ...action.payload, menuItem: [] } ]
};
default:
return state;
}
}
For the case ADD_CATEGORY in the Reducer, I am not sure what the correct syntax is for reassigning the menuArray property in the state to the modified array. I want the array to have the new object that is fetched within the addCategory action creator added to it.
When I run my application, I am getting the following error:
I am not sure why I am getting this error or how to resolve it. Any insights are appreciated.
Here menuArray: [ ...menuArray, { ...action.payload, menuItem: [] } menuArray variable is not defined which you are trying to spread. Use state.menuArray to access current menuArray.
Change:
menuArray: [ ...menuArray, { ...action.payload, menuItem: [] }
To:
menuArray: [ ...state.menuArray, { ...action.payload, menuItem: [] }
case ADD_CATEGORY:
return {
...state,
menuArray: [ ...state.menuArray, { ...action.payload, menuItem: [] } ]
};
I have the following state object in redux:
console.log({
jobOffers: {
filters: {
employments: [],
careerLevels: [],
jobTypeProfiles: [],
cities: [],
countries: [],
searchTerm: '',
currentPage: 1,
pageSize: 5
}
}
});
I want to set the array employments new.
That's my redux reducer:
export const reducer = (state = initialStateData, action) => {
switch (action.type) {
case Action.SET_ARR_FILTER:
{
const newNestedState = {
...state[action.key],
[action.key]: action.value,
};
return { ...state,
[action.key]: newNestedState
};
}
default:
return state;
}
};
The action:
export const SET_ARR_FILTER = 'SET_ARR_FILTER';
export const setEmployment = employment => ({
type: SET_ARR_FILTER,
key: 'employments',
value: employment,
});
But my object looks like this after the reducer has been called:
console.log({
employments: {
employments: ['HelloWorld']
},
})
What is wrong here ?
You're a level too deep (or not deep enough, depending on how you see it).
You need something like:
case Action.SET_ARR_FILTER:
{
const { filters } = state
return { ...state,
filters: {
...filters,
[action.key]: action.value
}
};
}
Similar to Mark's answer, all one line if you like.
export const reducer = (state = initialStateData, action) => {
switch (action.type) {
case Action.SET_ARR_FILTER:
return {
...state,
filter: {
...state.filter,
[action.key]: action.value
}
}
default:
return state;
}
};
Finally got it myself. Answer is:
case Action.SET_ARR_FILTER:
{
return {
...state,
jobOffers: {
...state.jobOffers,
filters: { ...state.jobOffers.filters,
[action.key]: action.value
},
},
};
}
Disclaimer: this question is targeting specific package reduxsauce
Takes classic redux action, by dispatching a single action, it will flow thru all the reducer and if we want to update the state, we catch the type in each and every reducer as we see fit
loginPage.js
this.props.memberLogin({ name: 'john' }); //{ type: MEMBER_LOGIN, payload: { name: 'john' } }
LoginAction.js
const memberLogin = member => {
return { type: MEMBER_LOGIN, payload: member }
}
authReducer.js
const INITIAL_STATE = { isLoggedIn: false }
switch(state = INITIAL_STATE, action) {
case MEMBER_LOGIN: return { ...state, isLoggedIn: true };
default: return state;
}
memberReducer.js
const INITIAL_STATE = { member: null }
switch(state = INITIAL_STATE, action) {
case MEMBER_LOGIN: return { ...state, member: action.payload };
default: return state;
}
Wondering by using reduxsauce, can we still achieve something similar as demonstrated above? (dispatch single action and update both reducer)
Yes, you can.
I created this Snack example to help you, but the gist is that you have to configure your reducers to listen to the same action.
Kinda like so:
const reduceA = (state, action) => ({
...state,
a: action.value,
});
const reduceB = (state, action) => ({
...state,
b: action.value,
});
const { Types, Creators: actionCreators } = createActions({
testAction: ['value'],
});
const HANDLERS_A = {
[Types.TEST_ACTION]: reduceA,
};
const HANDLERS_B = {
[Types.TEST_ACTION]: reduceB,
};
In the example both reducers A and B have their state values altered by the same action testAction.
I'm fairly new to redux & thunk, and have been following tutorials to try and understand, and am managing to work it into my app ok. One thing i'm not understanding, is how i can get several state objects on the root level into one nested object. For example, right now my state looks like:
{
timeline: [Array] // My timeline data in an array of objects
timelineHasErrored: false,
timelineIsLoading: false
}
But what I really want is:
{
timeline : {
data: [Array] // My timeline data in an array of objects
hasErrored: false,
isLoading: false
}
}
and i'm really not quite sure how to nest these, or what the proper way to do that is. Below is my redux code, it's pretty simple so i'll post it all.
Reducers index
import { combineReducers } from 'redux'
import { timeline, timelineHasErrored, timelineIsLoading } from './timeline'
export default combineReducers({
timeline, timelineHasErrored, timelineIsLoading
});
Timeline Reducers
import { TIMELINE_HAS_ERRORED, TIMELINE_IS_LOADING, TIMELINE_FETCH_DATA_SUCCESS } from '../constants/action-types.js'
export function timelineHasErrored(state = false, action) {
switch (action.type) {
case TIMELINE_HAS_ERRORED:
return action.hasErrored;
default:
return state;
}
}
export function timelineIsLoading(state = false, action) {
switch (action.type) {
case TIMELINE_IS_LOADING:
return action.isLoading;
default:
return state;
}
}
export function timeline(state = [], action) {
switch (action.type) {
case TIMELINE_FETCH_DATA_SUCCESS:
return action.timeline;
default:
return state;
}
}
Actions
import { TIMELINE_HAS_ERRORED, TIMELINE_IS_LOADING, TIMELINE_FETCH_DATA_SUCCESS } from '../constants/action-types.js'
import api from '../services/api'
export function timelineHasErrored(bool) {
return {
type : TIMELINE_HAS_ERRORED,
hasErrored : bool
}
}
export function timelineIsLoading(bool) {
return {
type : TIMELINE_IS_LOADING,
isLoading : bool
}
}
export function timelineFetchDataSuccess(timeline) {
return {
type : TIMELINE_FETCH_DATA_SUCCESS,
timeline
}
}
export function timelineFetchData() {
return dispatch => {
dispatch( timelineIsLoading(true) )
api.getTracks().then(
res => {
dispatch( timelineIsLoading(false) )
dispatch( timelineFetchDataSuccess(res.body) )
},
err => {
dispatch( timelineIsLoading(false) )
dispatch( timelineHasErrored(true) )
}
)
}
}
And then in my react component I format the object like how i want it... but i think it would be better to have it nested in the actual state so i'm not creating extra work for myself if things change
// Redux State
const mapStateToProps = (state) => {
const obj = {
timeline : {
data : state.timeline,
hasErrored: state.tracksHasErrored,
isLoading: state.tracksIsLoading
}
}
return obj
}
// Redux Dispatch
const mapDispatchToProps = (dispatch) => {
return {
fetchData: () => dispatch( timelineFetchData() )
}
}
If anybody has any tips or corrections for me bring em on, i'm trying to get a solid grasp on redux, thanks!
Your timeline reducer is pretty small, so you could have it as a single reducer as follows:
const initialState = {
data: [],
hasErrored: false,
isLoading: false
};
export function timeline(state = initialState, action) {
switch (action.type) {
case TIMELINE_HAS_ERRORED:
return {
...state,
hasErrored: action.hasErrored
};
case TIMELINE_IS_LOADING:
return {
...state,
isLoading: action.isLoading
};
case TIMELINE_FETCH_DATA_SUCCESS:
return {
...state,
data: action.timeline
};
default:
return state;
}
}
Then you wouldn't need to call combineReducers(), unless you had other reducers.