【问题标题】:Can I use useSelector() to return a deeper nested property/value of state?我可以使用 useSelector() 返回更深的嵌套属性/状态值吗?
【发布时间】:2021-01-01 00:38:47
【问题描述】:

来自 react-redux 官方docs

当一个action被派发时,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。我说的对吗?

【问题讨论】:

    标签: javascript reactjs redux react-redux


    【解决方案1】:

    doc 我们有:

    函数组件渲染时,将调用提供的选择器函数,并从 useSelector() 钩子返回结果。 (如果与之前渲染组件的函数引用相同,则挂钩可能会返回缓存的结果,而无需重新运行选择器。)

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

    顺便说一句,我们还有:

    当一个动作被分派到 Redux 存储时,useSelector() 仅在选择器结果看起来与上一个结果不同时才强制重新渲染。

    因为选择器函数的返回值是一个原始值——它是一个简单的字符串,不被识别为对象的一部分或与对象相关的东西(我认为这是你关心的问题)——正如你所说,它并没有改变场景,它会和上一个一样考虑,不会强制组件重新渲染。

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

    【讨论】:

      【解决方案2】:

      CodeSandbox link

      刚刚测试过,效果和预期一样。

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

      当然,要测试这种行为,你必须将它包装在 React.memo() 中,否则无论何时它的父级重新渲染,它都会重新渲染。


      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
      );
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-24
        • 1970-01-01
        • 2019-01-28
        • 1970-01-01
        • 1970-01-01
        • 2016-06-11
        • 1970-01-01
        • 2022-11-16
        相关资源
        最近更新 更多