我可以使用useSelector()返回更深层的嵌套属性/状态值吗?

时间:2020-09-14 09:37:59

标签: javascript reactjs redux react-redux

来自react-redux官方docs

enter image description here

调度一个动作时,useSelector()将对先前选择器结果值和当前结果值进行参考比较。如果它们不同,则将强制重新渲染组件。如果它们相同,则组件将不会重新渲染。

如果我做了这样的事情:

Redux状态

state = {
  groupAB: {
    propA: valueA,
    propB: valueB
  },
  propC,
  // ETC
}

SomeComponent.tsx

const propA = useSelector((state) => state.groupAB.propA);

如果:groupAB对象更改其引用(因为propB已突变)但propA: valueA保持不变,则该组件的行为如何?

我期望的是

即使groupAB发生更改,组件也不应重新渲染,因为在这个示例中,useSelector()关心的是返回值,即valueA。我说的对吗?

2 个答案:

答案 0 :(得分:1)

doc中,我们有:

当函数组件呈现时,将调用提供的选择器函数,并且其结果将从useSelector()挂钩返回。 (如果钩子返回的缓存结果与组件的先前渲染上的函数引用相同,则钩子可能会在不重新运行选择器的情况下返回它的缓存结果。)

您将箭头功能用作选择器功能,因此每个渲染中的引用都不会相同,并且永远不会从缓存中读取结果。

顺便说一下,我们还有:

当一个动作被分派给Redux的商店,useSelector()只迫使如果选择结果似乎比最后的结果不同重新绘制。

因为选择器函数的返回值是原始值-它是一个简单的字符串,未被识别为对象的一部分或与该对象相关的某些东西(我认为这是您的担忧),并且正如您所说的那样,它没有改变在这种情况下,它将考虑与上一个相同的情况,并且不会强制重新渲染该组件。

因此,最后,每次重新渲染组件时都会触发该函数,但是更改存储区的一部分而不导致更改所述原始值将不会以重新渲染组件结束。

答案 1 :(得分:0)

CodeSandbox link

经过测试,它的工作与预期的一样。

如果返回值不变,则组件将不会重新渲染。即使它是一个深层嵌套的值,例如示例state.groupAB.propA = "valueA"

当然,要测试此行为,您必须将其包装在React.memo()中,否则无论何时其父项重新渲染,它都将重新渲染。

enter image description here


App.js

import React from "react";
import "./styles.css";
import { useDispatch, useSelector } from "react-redux";
import SomeComponent from "./SomeComponent";

export default function App() {
  console.log("App rendering...");

  const state = useSelector((state) => state);
  const dispatch = useDispatch();

  function updateState() {
    dispatch({ type: "UPLOAD_GROUP_AB_AND_PROP_B" });
  }

  return (
    <div>
      <div>State: {JSON.stringify(state)}</div>
      <button onClick={updateState}>Update state</button>
      <SomeComponent />
    </div>
  );
}

reducer.js

const initialState = {
  groupAB: {
    propA: "valueA",
    propB: "valueB"
  },
  propC: "valueC"
};

export const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "UPLOAD_GROUP_AB_AND_PROP_B": {
      return {
        // WILL CREATE A NEW STATE OBJ
        ...state,
        groupAB: {
          // WILL CREATE A NEW groupAB OBJ
          ...state.groupAB, // propA WILL REMAIN THE SAME
          propB: "newValueB" // WILL UPDATE propB VALUE
        }
      };
    }
    default: {
      return state;
    }
  }
};

SomeComponent.js

import React from "react";
const { useSelector } = require("react-redux");

function SomeComponent() {
  console.log("SomeComponent rendering...");

  const propA = useSelector((state) => state.groupAB.propA);

  return (
    <div>
      <hr />
      <div>This is SomeComponent</div>
      state.groupAB.propA: {propA}
    </div>
  );
}

export default React.memo(SomeComponent);

index.js

import React from "react";
import ReactDOM from "react-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";

import App from "./App";
import { reducer } from "./reducer";

const store = createStore(reducer);

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);