Why useDispatch re-renders parent components?

Viewed 2121

I'm using useDispatch hook (from Redux) in onSelect callback in the Tree component (from Ant library):

export const MyComponent = () => {

    const dispatch = useDispatch();

    const onSelect = (selectedNode) => {
            const selectedItem = selectedNode[0];
            dispatch(fetchSelectedItems(selectedItem));
    };

    return 
        <Tree
            onSelect={onSelect}
        >
            <TreeNode .. />
            <TreeNode .. />
            <TreeNode .. />
        </Tree
}


export const fetchSelectedItems = (selected: string) =>
    (dispatch) =>
        axios({
            url: `/api/items?id=${selected}`,
            method: 'GET',
        }).then(response => {
            dispatch(fetchSelectedItemsSuccess(response.data))
        }).catch((error: any) => {throw(error)});

Why does useDispatch re-render parent components? Is there any way to prevent from this? I tried useCallback like it's in Redux documentation but this solution is to prevent child components from re-rendering, not parents.

3 Answers

It looks like my assumption in the comment was correct.

So I will show you the workaround.
You can extract the part that uses clickValue in the container to another component, say ClickValue.

Doing so will isolate the update to ClickValue component only.

My fork: https://codesandbox.io/s/soanswer60515755-9cc7u

function ClickValue() {
  const clickValue = useSelector(state => state.value);
  console.log(clickValue);
  return clickValue;
}

export default function Container() {
  return (
    <div className="Container">
      <h3>Container</h3>
      <ParentComponent />
      <ClickValue />
    </div>
  );
}

Check out the profile result below.

profile result

I would think that on every render you are redeclaring the onSelect function. Functions are reference types. Passing that redeclared function with its new reference on ever render will cause a rerender. Perhaps you should look into using context.

My problem with re-rendering components was caused by useSelector used in parent components where I directly refer to state. Most probably because of new result of this selector..

Solution: I rewrote this selectors with reselect library to make them memoized (it was suggested in one of comments here but I don't know why its've been removed). I did exactly what is in redux documentation about memoized selectors.

Related