Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent top scroll after update state

Hi I have problem with scroll to top page after every render. In my Root component, when I get Array of objects items from Redux Store. I filter mainArray on three subsArrays like : Hot, Favorite, Regular, then render each of them on specific route. The filter func is running each time when mainArray is updated: like rating is rise and down or set favorite will be marked.
The question is, why react render each times when action is dispatching to redux store(I think redux causes this,I guess) and how I can prevent this. Please give me a hint, I struggle with it for a while...

function Root() {
  const state = useSelector((state) => state);
  const { mainList: list } = state;

  const [regularList, setRegularList] = useState([]);
  const [hotList, setHotList] = useState([]);
  const [favoriteList, setFavoriteList] = useState([]);

const dispatch = useDispatch();

  const handleVote = (e) => {
    const currentId = Number(e.nativeEvent.path[3].id);
    const name = e.currentTarget.dataset.name;

    dispatch(listActions.vote(currentId, name));
  };

  const handleSetFave = (e) => {
    const currentId = Number(e.nativeEvent.path[3].id);
    dispatch(listActions.setFave(currentId));

const setArrays = (arr) => {
    const regularArr = arr.filter((meme) => meme.upvote - meme.downvote <= 5);
    const hotArr = arr.filter((meme) => meme.upvote - meme.downvote > 5);
    const favoriteArr = arr.filter((meme) => meme.favorite);

    setRegularList([...regularArr]);
    setHotList([...hotArr]);
    setFavoriteList([...favoriteArr]);
};

useEffect(() => {
    window.localStorage.getItem("mainList") &&
      dispatch(
        listActions.setMainList(
          JSON.parse(window.localStorage.getItem("mainList"))
        )
      );
  }, []);

  useEffect(() => {
    setArrays(list);
    list.length > 0 &&
      window.localStorage.setItem("mainList", JSON.stringify(list));
  }, [list]);

return (
    <div className={styles.App}>
      <Router>
        <Navigation />
        <Route path="/" component={FormView} exact />
        <Route
          path="/regular"
          component={() => (
            <MemesView
              list={regularList}
              handleVote={handleVote}
              handleSetFave={handleSetFave}
            />
          )}
        />
        <Route
          path="/hot"
          component={() => (
            <MemesView
              list={hotList}
              handleVote={handleVote}
              handleSetFave={handleSetFave}
            />
          )}
        />
        <Route
          path="/favorite"
          component={() => (
            <MemesView
              list={favoriteList}
              handleVote={handleVote}
              handleSetFave={handleSetFave}
            />
          )}
        />
      </Router>
    </div>
  );

  };
like image 563
edi_90 Avatar asked Jan 17 '26 12:01

edi_90


1 Answers

why react render each times when action is dispatching to redux store

Because you are subscribing to the whole redux state (by using useSelector((state) => state);), remember that each time an action is dispatched, redux computes a new state.

So you should not write const state = useSelector(state => state); otherwise your component will be rerendered each time an action is dispatched. Instead you must select the part of the state you are interested in.

I can deduce from your code you want to be notified every time there is a change on the mainList, so you can write :

const list = useSelector(state => state.mainList);

You can get more info by reading the documentation

by default useSelector() will do a reference equality comparison of the selected value when running the selector function after an action is dispatched, and will only cause the component to re-render if the selected value changed

Basically, the scroll to top page you are experiencing might also comes from a bad use of the Route component.

Try to use this

<Route path="/regular">
  <MemesView
    list={regularList}
    handleVote={handleVote}
    handleSetFave={handleSetFave}
  />
</Route>

instead of

<Route path="/regular"
       component={() => (
         <MemesView
           list={regularList}
           handleVote={handleVote}
           handleSetFave={handleSetFave}
         />
       )}
/>

Don't forget to also update /hot and /favorite routes.

You can read from the react router documentation

When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop (below).

like image 157
Olivier Boissé Avatar answered Jan 20 '26 02:01

Olivier Boissé