【问题标题】:dynamically rendered components with context and react.memo does not update state accordingly具有上下文和 react.memo 的动态渲染组件不会相应地更新状态
【发布时间】:2021-01-13 18:45:21
【问题描述】:

我想基于 JSON 文档呈现组件。我试图用钩子和上下文来解决这个问题。因为组件有一个状态,并且我不想在组件的一个状态发生变化时重新渲染所有组件,所以我使用 React.memo 来防止重新渲染。因此,我提升了所有状态,因此我可以在顶级钩子中管理状态并将所有内容作为道具传递。

现在,当我想从组件内部更改状态时,React.memo 似乎记住了旧状态,并且状态没有按预期更新。 因为有点难解释,所以我提供了一个例子codesandbox。如果您单击不同颜色的 div,您将直接看到问题。我还提供了一些控制台输出来显示只有被点击的组件会重新渲染。

如何解决只有被点击的组件在考虑新更新状态的情况下重新渲染的问题?

另外,这里是App.tsx 的完整代码(与代码沙箱中相同)

import * as React from "react";

type DivTemplate = {
  [key: string]: {
    type: "green" | "red" | "blue";
  };
};

type DivDoc = { [key: string]: number };

const TripleDivObj: DivTemplate = {
  green_div: {
    type: "green"
  },
  red_div: {
    type: "red"
  },
  blue_div: {
    type: "blue"
  }
};

type ColorDivProps = {
  id: string;
  color: "green" | "red" | "blue";
  count: number;
  incrementValue: () => void;
};

const ColorDiv = React.memo(
  (props: ColorDivProps) => {
    console.log(`Hook re-render ${props.color}. Value ${props.count}`);

    return (
      <div
        style={{
          background: props.color,
          width: 300,
          height: 300,
          fontSize: 32,
          color: "white",
          display: "grid",
          placeItems: "center"
        }}
        onClick={() => props.incrementValue()}
      >
        {props.count}
      </div>
    );
  },
  (prevProps, nextProps) => prevProps.count === nextProps.count
);

function TripleDiv() {
  const { getCount, incrementValue } = useTripleDivHookContext();
  return (
    <div
      style={{
        width: "100vw",
        display: "flex",
        justifyContent: "space-around"
      }}
    >
      {Object.entries(TripleDivObj).map(([id, value]) => {
        return (
          <ColorDiv
            key={id}
            id={id}
            color={value.type}
            count={getCount(id)}
            incrementValue={() => incrementValue(id)}
          />
        );
      })}
    </div>
  );
}

const TripleDivHookContext = React.createContext<ReturnType<
  typeof useTripleDiv
> | null>(null);

const useTripleDivHookContext = () => {
  const value = React.useContext(TripleDivHookContext);
  if (value === null) throw Error("Context is null");
  return value;
};

const useTripleDiv = () => {
  const _divState: DivDoc = {};
  for (const key of Object.keys(TripleDivObj)) {
    _divState[key] = 0;
  }
  const [divState, setDivState] = React.useState<DivDoc>(_divState);

  const incrementValue = (id: string) => {
    console.log(`about to increment: ${id}`);
    console.log(`state: ${JSON.stringify(divState)}`);
    if (id in divState) {
      console.log(`increment ${id}`);
      setDivState({ ...divState, [id]: divState[id] + 1 });
    }
  };

  function getCount(id: string) {
    if (id in divState) return divState[id];
    return 0;
  }

  return { getCount, incrementValue };
};

function HookBasedDivs() {
  const hook = useTripleDiv();

  return (
    <TripleDivHookContext.Provider value={hook}>
      <TripleDiv />
    </TripleDivHookContext.Provider>
  );
}

export default function App() {
  return (
    <div>
      <h1>Hooks</h1>
      <HookBasedDivs />
    </div>
  );
}

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    我不完全理解,但是当你替换第 101 行时它可以工作

    setDivState({ ...divState, [id]: divState[id] + 1 });
    

    setDivState((prevDivState) => {
      return { ...prevDivState, [id]: prevDivState[id] + 1 };
    });
    

    【讨论】:

    • 将函数传递给setState 是编写依赖于先前状态的 setState 调用的推荐格式。这是因为状态更新是异步的。您可以在React docs here 中看到这一点
    猜你喜欢
    • 2019-06-17
    • 2019-09-27
    • 2021-09-21
    • 2019-10-28
    • 1970-01-01
    • 2021-05-19
    • 2022-01-26
    • 2019-06-29
    • 2020-08-18
    相关资源
    最近更新 更多