无法让 React.useCallback 作为命名导出工作

IT技术 reactjs react-hooks
2021-04-30 08:37:52

在下面的代码段中,当传递上下文的值时,useCallback 会阻止所有按钮按预期重新渲染。clickFunction缓存。

Stackblitz:https ://stackblitz.com/edit/react-wnfesi

clickFunctionContext.js

import { GlobalContext } from "./GlobalState";

export const ClickFuncContext = createContext();

export const ClickFuncProvider = ({ children }) => {
  const { speakerList, setSpeakerList } = useContext(GlobalContext);

  const clickFunction = useCallback(speakerIdClicked => {
    setSpeakerList(currentState => {
      return currentState.map(rec => {
        if (rec.id === speakerIdClicked) {
          return { ...rec, favorite: !rec.favorite };
        }
        return rec;
      });
    });
  }, []);

  return (
    <ClickFuncContext.Provider value={clickFunction}>
      {children}
    </ClickFuncContext.Provider>
  );
};

也就是说,当我从下面的文件中引用这个上下文时,只有我点击的按钮被渲染,而不是其他按钮。

演讲者.js

import React, { useContext } from "react";

import { ClickFuncContext } from "./clickFunctionContext";

export default React.memo(({ speaker, clickFunction }) => {
  console.log(`speaker ${speaker.id} ${speaker.name} ${speaker.favorite}`);
  const clickFunction = useContext(ClickFuncContext)

  return (
    <>
      <button
        onClick={() => {
          clickFunction(speaker.id);
        }}
      >
        {speaker.name} {speaker.id}{" "}
        {speaker.favorite === true ? "true" : "false"}
      </button>
    </>
  );
});

但是,我需要的是将 useContext 中的 clickFunction 作为属性而不是值返回。

所以,我需要改变这一行:

const clickFunction = useContext(ClickFuncContext)

到:

const { clickFunction } = useContext(ClickFuncContext)

而且,在clickFunctionContext.js我改变这一行:

<ClickFuncContext.Provider value={{clickFunction}}>

到:

<ClickFuncContext.Provider value={clickFunction}>

该应用程序可以工作,但所有按钮都重新呈现,就好像我没有完成回调一样。

感谢理解为什么这与没有额外参考的情况不同。

这是损坏的 stackblitz:https ://stackblitz.com/edit/react-b6jrt8


这是 stackblitz 中的解决方案,如下所述。https://stackblitz.com/edit/react-pdglx5

2个回答
value={{clickFunction}}

这将创建一个具有 clickFunction 属性的新对象。由于它是一个新对象,它在一次渲染和下一次渲染之间总是不同的,因此 react 被迫将上下文通知所有消费者。clickFunction 可能是一样的,但是对象不是,react 只是做一个===比较。

如果您需要为您的上下文值设置一个对象,您应该记住该对象,以便它在需要时不会更改。

const value = useMemo(() => {
  return { clickFunction };
}, [clickFunction]);

// ...
<ClickFuncContext.Provider value={value}/>

您可以在react 的上下文文档中看到有关此主题的更多信息

你好,我不知道你为什么要返回一个函数并将它作为对象传递给 props,如果你想这样做,只需使用useMemo而不是useCallback这样:

const clickFunction = useMemo(
  () => ({
    clickFunction: function(speakerIdClicked) {
      setSpeakerList(currentState => {
        return currentState.map(rec => {
          if (rec.id === speakerIdClicked) {
            return { ...rec, favorite: !rec.favorite };
          }
          return rec;
        });
      });
    }
  }),
  []
);