Search code examples
reactjsreduxredux-thunkredux-toolkit

How to dispatch error action within the reducer?


I'm learning Redux + Redux Toolkit. And I need a piece of advice.

Very basic example. I have some state slice:

const postsSlice =  createSlice({
  name: 'posts',
  initialState: [],
  reducers: {
    // ... some other reducers

    updatePost(state, payload) {
      const { id, title, content } = payload;
      const existingPost = state.find(post => post.id === id);

      if (existingPost) {
        existingPost.title = title;
        existingPost.content = content;
      } else {
        // I want to dispatch some error action because user tries to edit a post that does not exist!
      }
    }
  }
});

So I have the updatePost reducer which I export as action creator. It updates the post with given id. If post with id not found, I want to show the error message. Let's say I have another state slice for messages with a corresponding action. But how I can dispatch it from my reducer? And should I? It feels like an anti-pattern for me.

So far I'm thinking to export a wrapper (thunk?) for my updatePost action creator. Something like this:

export const updatePost = payload => (dispatch, getState) => {
  const { id } = payload;
  const existingPost = getState().posts.find(post => post.id === id);
  
  if (existingPost) {
    dispatch(postsSlice.actions.updatePost(payload));
  } else {
    dispatch(showError('some invalid post error'));
  }
};

This solution looks very ugly for me. First of all it operates on the whole store state (getState()). And also, I'm not sure if this is what I should use thunks for. It looks like they are made more for stuff like async data fetching primary.


Solution

  • But how I can dispatch it from my reducer? And should I? It feels like an anti-pattern for me.

    You are right here. You should never dispatch an action from the reducer.

    Your thunk example isn't bad. But since you are asking for opinions, my personal opinion is that the Component which handles editing a post should be responsible for making sure that the post exists (which you would do by using a selector). If the post id is invalid, we should prevent the editing from ever happening. There would be no need to dispatch an invalid post error as an action, that would be something which is handled internally in the Component.

    const PostEditor = ({id}) => {
    
        const current = useSelector(getPost(id));
    
        const dispatch = useDispatch();
    
        return (
            <div>
                {!! current ? 
                    <EditPost ..../>
                    :
                    <div>Error: Invalid Post ID #{id}</div>
                }
            </div>
        )
    }