如何防止通过重新选择进行重新渲染?

时间:2020-03-16 07:47:32

标签: javascript reactjs redux reselect

我今天尝试使用reselect中间件,以防止不必要的重新渲染。

这是我的reducer.js:

const INITIAL_STATE = {
  dogs: 100,
  cats: 12312302384
};

const pets = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case "CHANGE_DOGS":
      return {
        ...state, dogs: state.dogs + 1
      };
    case "CHANGE_CATS":
      return {
        ...state, cats: state.cats + 1
      };
    default:
      return { ...state };
  }
};

export default pets;

这是我的main.js:

import React from "react";
import { createSelector } from "reselect";
import { useSelector, useDispatch } from "react-redux";

function ReduxDeneme(props) {
  // Selectors - Classic style - NON Memoized!
//   const dogsData = useSelector(state => state.pets.dogs);
//   const catsData = useSelector(state => state.pets.cats);

  //                          Dogs rendering..  --> First opening..
  // 10:11:28.070 index.js:18 CATS rendering.. --> First opening..
  // 10:11:29.703 index.js:13 Dogs rendering.. --> Press "ChangeDogs" button.
  // 10:11:29.703 index.js:18 CATS rendering..  --> Press "ChangeDogs" button.
  // 10:11:33.143 index.js:13 Dogs rendering.. --> Press "ChangeCats" button.
  // 10:11:33.143 index.js:18 CATS rendering..  --> Press "ChangeCats" button.

  // Selectors - Memoized version RESELECT middleware'i ile..
  const dogsDataMemo = createSelector(
    state => state.pets.dogs,
    dogs => dogs
  );
  const catsDataMemo = createSelector(
    state => state.pets.cats,
    cats => cats
  );

  const dogsData = useSelector(dogsDataMemo)
  const catsData = useSelector(catsDataMemo)


  // Components
  const Dogs = ({ dogsData }) => {
    console.log("Dogs rendering..");
    return <h1>{dogsData}</h1>;
  };

  const Cats = ({ catsData }) => {
    console.log("Cats rendering..");
    return <h1>{catsData}</h1>;
  };

  // Actions

  const dispatch = useDispatch();
  const changeDogs = () => dispatch({ type: "CHANGE_DOGS" });
  const changeCats = () => dispatch({ type: "CHANGE_CATS" });

  return (
    <div>
      <Dogs dogsData={dogsData} />
      <Cats catsData={catsData} />
      <button onClick={changeDogs}>Change Dogs</button>
      <button onClick={changeCats}>Change CATS</button>
    </div>
  );
}

export default ReduxDeneme;
  • 1。场景:我使用了经典的非记忆式样式选择器。控制台上有6次console.log。 (通常是第一次打开2次,单击“更改狗”按钮2次,单击“更改猫”按钮2次)。这意味着发生了6次重新渲染。
  • 2。场景:我已经使用reselect中间件来防止不必要的重新渲染。但是,它不起作用,或者我误解了reselect的含义。

有人可以解释正确的方法还是我在哪里做错了?

2 个答案:

答案 0 :(得分:1)

您正在定义组件内部的选择器。您应该在室外(例如减速机附近的地方)进行操作。

当前,您正在每个渲染之后重新创建选择器。这是一种更好的方法:

// inside reducer.js

  export const petsSel = state => state.pets;
  export const dogsDataMemo = createSelector(
    petsSel,
    pets => pets.dogs
  );
  export const catsDataMemo = createSelector(
    petsSel,
    pets => pets.cats
  );

根据您的代码https://codesandbox.io/s/delicate-snowflake-5ssrw

添加了一个带有示例的代码框。

要实现您想要的目标,还需要使用React.memo(https://reactjs.org/docs/react-api.html#reactmemo):

const Dogs = React.memo(({ dogsData }) => {
  console.log("Dogs rendering..");
  return <h1>{dogsData}</h1>;
});

答案 1 :(得分:1)

首先,非常感谢@tudor的努力。他的所有话都是正确的。

但是,我想证明重新选择有效。

场景1-非记忆

import React, { memo } from "react";
import { useSelector, useDispatch } from "react-redux";
// import { catsDataMemo, dogsDataMemo } from "./selectors";

// Components
const Dogs = memo(({ dogsData }) => {
  console.log("Dogs rendering..");
  return <h1>{dogsData}</h1>;
});

const Cats = memo(({ catsData }) => {
  console.log("Cats rendering..");
  return <h1>{catsData}</h1>;
});

function ReduxDeneme() {
  // Standart useSelector without MEMOIZED
  const dogsData = useSelector(
    state => state.pets.dogs,
    console.log("dogsData Selector çalıştı.")
  );
  const catsData = useSelector(
    state => state.pets.cats,
    console.log("catsData Selector çalıştı.")
  );

  // Actions

  const dispatch = useDispatch();
  const changeDogs = () => dispatch({ type: "CHANGE_DOGS" });
  const changeCats = () => dispatch({ type: "CHANGE_CATS" });

  const noChangeCats = () =>
    dispatch({ type: "NO_CHANGE_CATS", payload: catsData });

  return (
    <div>
      <Dogs dogsData={dogsData} />
      <Cats catsData={catsData} />
      <button onClick={changeDogs}>Change Dogs</button>
      <button onClick={changeCats}>Change CATS</button>
      <button onClick={noChangeCats}>No Change</button>
    </div>
  );
}

export default memo(ReduxDeneme);

小心!!当您单击“ 更改狗狗”按钮时,控制台中的输出将为:

dogsData Selector çalıştı.
catsData Selector çalıştı.
Dogs rendering..

或单击“ 更换猫咪”按钮时,输出为:

dogsData Selector çalıştı.
catsData Selector çalıştı.
Cats rendering..

无论您按下什么按钮,两个useSelector都可以正常工作,就像您从console.log中看到的一样。

场景2-通过重新选择中间件记忆

首先,我们将记忆选择器分离到另一个文件,如@ tudor.gergely所述。

小心!您必须定义对象的正确路径。

// selectors.js

import { createSelector } from "reselect";

export const dogsDataMemo = createSelector(
  state => state.pets.dogs, // BE CAREFULL while defining..
  dogs => {
    console.log("DogsDataMemo has worked.");
    return dogs;
  }
);
export const catsDataMemo = createSelector(
  state => state.pets.cats, // BE CAREFULL while defining..
  cats => {
    console.log("CatsDataMemo has worked.");
    return cats;
  }
);

然后,我们将此文件导入main.js文件,并再次将useSelector与我们记住的选择器一起使用:

import React, { memo } from "react";
import { useSelector, useDispatch } from "react-redux";
import { catsDataMemo, dogsDataMemo } from "./selectors";

// Components
const Dogs = memo(({ dogsData }) => {
  console.log("Dogs rendering..");
  return <h1>{dogsData}</h1>;
});

const Cats = memo(({ catsData }) => {
  console.log("Cats rendering..");
  return <h1>{catsData}</h1>;
});

function ReduxDeneme() {
  const dogsData = useSelector(dogsDataMemo);
  const catsData = useSelector(catsDataMemo);

  // Actions
  const dispatch = useDispatch();
  const changeDogs = () => dispatch({ type: "CHANGE_DOGS" });
  const changeCats = () => dispatch({ type: "CHANGE_CATS" });

  const noChangeCats = () =>
    dispatch({ type: "NO_CHANGE_CATS", payload: catsData });

  return (
    <div>
      <Dogs dogsData={dogsData} />
      <Cats catsData={catsData} />
      <button onClick={changeDogs}>Change Dogs</button>
      <button onClick={changeCats}>Change CATS</button>
      <button onClick={noChangeCats}>No Change</button>
    </div>
  );
}

export default memo(ReduxDeneme);

和最终输出:

  • 单击“更改狗”按钮时:
DogsDataMemo has worked.
Dogs rendering
  • 单击“更换猫”按钮时:
CatsDataMemo has worked.
Cats rendering..