Getting a ID back from a reducer in redux

2019-05-01 03:34发布

问题:

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:

  1. I know how the bookmark-ID is generated in the reducer (via 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.
  2. Bookmarks and folders are kept in the same state-branch (same reducer), because they are both just "Marks", I could have a state-property that references the latest added bookmark, but this also 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.

回答1:

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;
} 


回答2:

Reducers are just manipulating your store's instance. You may the current snapshot of your state using getState()