ram
ram

Reputation: 690

Bad idea to put a dom operation inside a redux reducer?

I have several actions which use the same reducer, and instead of having a dom operation in each of those actions, I want to just add it once inside my shared reducer. I know reducers are to be pure (which the returned data still is), but is this some kind of anti-pattern or an acceptable strategy?

 case APPEND_POSTS:
      !payload.length &&
        document.getElementById('posts-cont').classList.add('no-more-posts'); // this 
      const total = state.posts.length + payload.length;
      const limit = total > posts_to_keep_limit ? 50 : 0;
      return {
        ...state,
        posts: [...state.posts.slice(limit), ...payload],
        loading: false,
      };
    ```

Upvotes: 4

Views: 444

Answers (3)

Rodrigo Amaral
Rodrigo Amaral

Reputation: 1382

jinongun's advice is good: let the className of the component derive its value from the store's state using a selector. AS for the general question

I have several actions which use the same reducer, and instead of having a dom operation in each of those actions, I want to just add it once inside my shared reducer.

DON'T EVER make DOM operations inside a reducer. Don't ever make any operation that is not a pure computation.

But you can create an action creator that always calls a side effect (with Redux-Thunk):

function appendPosts(payload) {
  return dispatch => {
    mySideEffect()

    dispatch({
      type: APPEND_POSTS,
      payload
    })
  }
}

function action1(params) {
  return dispatch => {
    dispatch({
      type: ACTION1,
      payload: params
    })    

    dispatch(appendPosts(params))
  }
}

function action2(params) {
  return dispatch => {
    dispatch({
      type: ACTION2,
      payload: params
    })    

    dispatch(appendPosts(params))
  }
}

// etc

Upvotes: 1

Jim G.
Jim G.

Reputation: 15363

I know reducers are to be pure (which the returned data still is), but is this some kind of anti-pattern or an acceptable strategy?

The returned data is pure, but you've introduced a side-effect in the form of a DOM mutation. Therefore, this reducer is not pure.

This is indeed an anti-pattern because now, the component(s) that render posts-cont items have an invisible coupling to this reducer. It makes your codebase more difficult to read and debug.

Upvotes: 3

kyun
kyun

Reputation: 10264

Redux Action

 case APPEND_POSTS:
      // you don't need to use below code.
      // !payload.length && document.getElementById('posts-cont').classList.add('no-more-posts'); // this 
      const total = state.posts.length + payload.length;
      const limit = total > posts_to_keep_limit ? 50 : 0;
      return {
        ...state,
        posts: [...state.posts.slice(limit), ...payload],
        nomore: true,
        loading: false,
      };

Your component.

function YourComp(props){
  const state = useSelector(...); 

  return ( <div id="posts-cont" className={state.nomore ? 'no-more-posts' : ''} > {...}</div>

 
} 

Upvotes: 4

Related Questions