I'm fairly new and trying to build a simple bookmark application with react & redux.
I can't spin my head around this problem:
A user can create one bookmark and add it to multiple folders. So I dispatch an addMark(bookmark)
action, and after that addMark(folder)
or editFolder(folder)
if the folder already exists. As you can see, bookmark and folder are added via the same action, because in my state tree they are both just marks - distinguished by their type property.
My problem: How can I tell the folder-objects which is the new bookmark to add to folders list of bookmarks? How can I retrieve the ID of the newly created bookmark between the two dispatches?
Solutions I don't find satisfying:
Math.max
over the existing bookmark IDs), so I can reproduce the new bookmark ID between the 2 dispatches.This sounds like a bad hack.A little bit of source code, to understand what I have:
// mapping between dispatcher and props to my react view
const mapDispatchToProps = (dispatch) => ({
saveMark: (mark) => {
if (mark.id) {
dispatch(editMark(mark));
} else {
dispatch(addMark(mark));
}
},
});
export default connect(mapStateToProps, mapDispatchToProps)(AddMark);
And Inside AddMark, which is the container component:
// save the bookmark first
this.props.saveMark({
type: 'bookmark',
title: this.state.title,
url: this.state.url,
icon: this.props.icon,
style: this.state.style,
});
// now I need the bookmark ID
folders.forEach(folder => {
folder.children.push(bookmarkID) // <-- !!!
});
folders.forEach(folder => this.props.saveMark(folder));
I can't find a satisfying solution for this.
Reducers always have to return something even if it's null ; they should never return undefined . If a reducer's state is an object, you must always return a new object instead of editing the object in place. createStore() requires a single argument, which is the "root reducer" and returns a store .
In redux, Updation of state happens in the reducer function. Basically reducer function returns a new state by performing an action on the initial state.
You are only disallowed to dispatch inside the reducers because they must have no side effects. If you want to cause a side effect in response to an action, the right place to do this is in the potentially async action creator.
I think that you should dispatch only one action here: addBookmark()
, which accepts both bookmark object and folder.
Your code, which handles adding bookmark object into folder should be part of reducer.
Also, refer the Todos example in Redux project. It has id provided in action creation to make it possible to read it in the component. You can also use current state to compute latest id:
function addBookmark(bookmark, folder) {
return (dispatch, getState) => {
const newBookmark = Object.assign({
id: Math.max(0, ...getState().bookmarks.map(b => b.id)) + 1,
}, bookmark);
dispatch({
type: 'ADD_BOOKMARK',
bookmark: newBookmark,
folder: folder
});
}
}
Notice that example requires redux-thunk middleware to dispatch those actions.
Then you can get access to bookmark with id in the folders reducer
function folderReducer(state = [], action) {
if(action.type === 'ADD_BOOKMARK') {
return state.map(folder => {
if(folder === action.folder) {
return Object.assign({}, folder, {children: [...folder.children, action.bookmark.id]}
}
return folder;
})
}
//another reducer code
return state;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With