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
},
},
};
}
Related
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
So i just learned redux and tried to make a simple list with redux and react.
but when i click on the button to add item to the list i got an error "state is not iterable"
here is my code
reducer
function manageList(state = { items: [] }, action) {
switch (action.type) {
case ADD_ITEM:
return { list: [...state, action.payload] };
case RESET_LIST:
return {
item: [...state, []],
};
default:
return state;
}
}
action
export const ADD_ITEM = "ADD_ITEM";
export const RESET_LIST = "RESET_LIST";
export function addItem(text) {
return { type: ADD_ITEM, payload: text };
}
export function resetList() {
return { type: RESET_LIST };
}
You're spreading an object inside an array, to fix that you should spread the items property inside an array:
function manageList(state = { items: [] }, action) {
switch (action.type) {
case ADD_ITEM:
return { list: [...state.items, action.payload] };
case RESET_LIST:
return {
items: [...state.items, []],
};
default:
return state;
}
}
I think also that you should replace list and item by items :
function manageList(state = { items: [] }, action) {
switch (action.type) {
case ADD_ITEM:
return { items: [...state.items, action.payload] };
case RESET_LIST:
return {
items: [...state.items, []],
};
default:
return state;
}
}
I think you should spread them as "state.items" not as just "state".
Like this:
item: [...state.items,[]]
Not like this:
item: [...state,[]]
export const commentReducer = (state = initialState, action) => {
switch (action.type) {
case COMMENT_REQUEST:
return {
...state,
loading: true,
};
case COMMENT_SUCCESS:
return {
...state,
comment: [...state.comment, action.payload],
};
case COMMENT_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
It works for me
I'm trying to create a delete_TODO dispatch function in redux and I got an error: "TypeError: state.byIds.filter is not a function"
I don't exactly understand why someone can explain me, I believe its a small stupid thing but I don't see
import { ADD_TODO, TOGGLE_TODO, DELETE_TODO } from "../actionTypes";
const initialState = {
allIds: [],
byIds: {},
};
export default function (state = initialState, action) {
switch (action.type) {
case ADD_TODO: {
const { id, content } = action.payload;
return {
...state,
allIds: [...state.allIds, id],
byIds: {
...state.byIds,
[id]: {
content,
completed: false,
},
},
};
}
case TOGGLE_TODO: {
const { id } = action.payload;
return {
...state,
byIds: {
...state.byIds,
[id]: {
...state.byIds[id],
completed: !state.byIds[id].completed,
},
},
};
}
//here is the probleme
case DELETE_TODO: {
const { id } = action.payload;
return {
...state,
allIds: state.allIds.filter((todo) => todo !== id),
byIds: state.byIds.filter((todo) => todo !== id),
};
}
default:
return state;
}
}
Do you mean your byIds property in your initial state is an array type instead of object since .filter is not a part of literal object prototype?
const initialState = {
allIds: [],
byIds: [],
};
state.byIds is an object not array, filter works on array not object.
you have to make byIds an array. Try this:
import { ADD_TODO, TOGGLE_TODO, DELETE_TODO } from "../actionTypes";
const initialState = {
allIds: [],
byIds: [],
};
export default function (state = initialState, action) {
switch (action.type) {
case ADD_TODO: {
const { id, content } = action.payload;
return {
...state,
allIds: [...state.allIds, id],
byIds: [
...state.byIds,
[id]: {
content,
completed: false,
},
],
};
}
case TOGGLE_TODO: {
const { id } = action.payload;
return {
...state,
byIds: [
...state.byIds,
[id]: {
...state.byIds[id],
completed: !state.byIds[id].completed,
},
],
};
}
//here is the probleme
case DELETE_TODO: {
const { id } = action.payload;
return {
...state,
allIds: state.allIds.filter((todo) => todo !== id),
byIds: state.byIds.filter((todo) => todo !== id),
};
}
default:
return state;
}
}
Let's say I have normalized state like this (using normalizr)
entities: {
todos: {
'aaa': {
id: 'aaa',
text: 'Todo A',
completed: false
},
'bbb': {
id: 'bbb',
text: 'Todo B',
completed: false
},
'ccc': {
id: 'ccc',
text: 'Todo C',
completed: false
}
}
}
Then I have an action that fetches array of IDs from the server, that are completed. For that action type, I have reducer like this:
const todos = (state = initialState, action) => {
switch (action.type) {
case ActionTypes.FETCH_COMPLETED_SUCCESS:
return {
...state,
todos: action.payload.completedIds.map((id) => {
return {
[id]: {
...state.todos[id],
completed: true
}
}
})
};
default:
return state;
}
};
If I recieved an array with ['aaa', 'ccc'] (could be thousands of items in real world app), I want to set "completed" to TRUE on those respective todos in a single action, is it possible?
My current implementation of reducer doesn't work since it returns an array of objects, while original normalized state is object with IDs as key.
Thanks.
You could store the modified todos in an object and then update it using the spread syntax like
const todos = (state = initialState, action) => {
switch (action.type) {
case ActionTypes.FETCH_COMPLETED_SUCCESS:
let newTodos = {};
action.payload.completedIds.forEach((id) => {
newTodos[id]: {
...state.todos[id],
completed: true
}
})
})
return {
...state,
todos: {
...state.todos,
...newTodos
}
};
default:
return state;
}
};
I think this is the solution for your problem:
const todos = (state = initialState, action) => {
switch (action.type) {
case ActionTypes.FETCH_COMPLETED_SUCCESS:
return {
...state,
todos: Object.keys(action.payload.completedIds).reduce((previous, current) => {
previous[current] = {
...state.todos[current],
completed: true
}
return previous;
}, {})
})
};
default:
return state;
}
};
I used this solution because returns a new object and leaves the original object as it is
You can try this:
const todos = (state = initialState, action) => {
switch (action.type) {
case ActionTypes.FETCH_COMPLETED_SUCCESS:
const newState = {};//New Object
const filterKeys = Object.keys(state).filter( key=>{
newState[key]=state[key]; //copy
return completedIds.indexOf(key)!=-1;
});
filterKeys.forEach(key=> newState[key].completed = true);
return newState;
default:
return state;
}
};
I'm a newbie in redux and es6 syntax. Here the problem:
There is an app with multiple posts.
const initialState = {
items: {
3: {title: '1984'},
6: {title: 'Mouse'},
19:{title: 'War and peace'}
}
}
App receive an array of liked posts ids:
dispatch(receiveLikedPosts(3, {id:3, ids: [3,6]}));
function receiveLikedPosts(ids) {
return {
type: LIKED_POSTS_RECEIVED,
ids
};
}
There is a posts reducer:
function posts(state = initialState, action) {
switch (action.type) {
case LIKED_POSTS_RECEIVED:
// here I need to update my posts state: post.liked => true (only 3 and 6 post)
default:
return state;
}
}
1) I have to update my reducers LIKED_POSTS_RECEIVED code. Dunno how to make it in right way.
2) Is it correct to dispatch events multiple times? ( one dispatch for each liked post)
Here the code:
// action
let ids = [3,6]
for (let id of ids) {
dispatch({type: LIKE, id});
}
// reducers
function post(state, action) {
switch (action.type) {
case LIKE:
return Object.assign({}, state, {
liked: true
});
default:
return state;
}
}
function posts(state = initialState, action) {
switch (action.type) {
case LIKE:
return Object.assign({}, state, {
[action.id]: post(state[action.id], action)
});
default:
return state;
}
}
This is confusing to me:
dispatch(receiveLikedPosts(3, {id:3, ids: [3,6]}));
function receiveLikedPosts(ids) {
return {
type: LIKED_POSTS_RECEIVED,
ids
};
}
Your function receiveLikedPosts only accepts one argument, yet you're passing it two. And I'm not sure what { id: 3, ids: [3, 6] } is supposed to be doing. But, here's what I would do:
Initial state and reducer:
const initialState = {
items: {
3: { title: '1984', liked: false },
6: { title: 'Mouse', liked: false },
19: { title: 'War and peace', liked: false }
}
};
function posts(state = initialState, action) {
switch (action.type) {
let newItems = {};
case LIKED_POSTS_RECEIVED:
// copy the current items into newItems
newItems = {...state.items};
// Loop through the liked IDs, set them to liked:true
action.ids.forEach((likedId) => {
newItems[likedId].liked = true;
});
// Return the new state
return {
...state,
items: newItems,
}
default:
return state;
}
}
Action creator:
function receiveLikedPosts(ids) {
return {
type: LIKED_POSTS_RECEIVED,
ids,
};
}
And finally, the dispatch:
dispatch(receiveLikedPosts([3, 6]));