问题描述
我正在尝试基于 useContext 创建一个自定义钩子 useFocus 以仅将焦点设置在我选择的组件上。 它可以工作,但即使我使用 useCallback 作为我的 useFocus 自定义钩子返回的函数,其他组件也在呈现。
我只想重新渲染焦点不断变化的组件。
我知道如果代码很快,重新渲染可能是小问题,但我不明白为什么要重新渲染。 你能给我一些解释或修复吗。
预期结果:
当点击“设置焦点”按钮时,我希望得到:
1 个 A/B/D 渲染
2 个 C/E 渲染
谢谢。
这是我的代码:
import React,{ createContext,useCallback,useContext,useState } from "react";
import "./styles.css";
const StepContext = createContext({});
//This is just to display number or render for each Items
function useRenderCounter() {
const ref = React.useRef();
React.useEffect(() => {
ref.current.textContent = Number(ref.current.textContent || "0") + 1;
});
return (
<span
style={{
backgroundColor: "#ccc",borderRadius: 4,padding: "2px 4px",fontSize: "0.8rem",margin: "0 6px",display: "inline-block"
}}
ref={ref}
/>
);
}
const useFocus = (property) => {
const context = useContext(StepContext);
const bool = context === property;
//console.log("bool",bool,context,property);
//return bool
return useCallback(() => bool,[bool]);
};
const Item = React.memo(({ property }) => {
const rendercounter = useRenderCounter();
const isFocus = useFocus(property);
//Here I expect to got re-render only for property which the focus changed
const focus = isFocus();
console.log(property,"render",focus);
const style = focus ? { borderStyle: "solid",borderColor: "red" } : {};
return (
<div style={{ display: "flex",margin: "4px" }}>
{rendercounter}
<div style={style}>{property}</div>
</div>
);
});
export default function App() {
const [focusOn,setFocusOn] = useState("E");
const handleClick = () => setFocusOn("C");
return (
<StepContext.Provider value={focusOn}>
<div style={{ display: "flex",flexDirection: "column" }}>
<Item key={1} property={"A"} />
<Item key={2} property={"B"} />
<Item key={3} property={"C"} />
<Item key={4} property={"D"} />
<Item key={5} property={"E"} />
<button onClick={handleClick}>set focus</button>
</div>
</StepContext.Provider>
);
}
解决方法
当 Provider 获得新值时,无法避免重新渲染。来自official docs on Context API:
当 Provider 的 value prop 发生变化时,所有作为 Provider 后代的消费者都会重新渲染。从 Provider 到其后代消费者(包括 .contextType 和 useContext)的传播不受 shouldComponentUpdate 方法的约束,因此即使祖先组件跳过更新,消费者也会更新。