如何使用自定义钩子和 useContext 仅渲染相关组件

Posted

技术标签:

【中文标题】如何使用自定义钩子和 useContext 仅渲染相关组件【英文标题】:How to render only the concerned component with a custom hook and useContext 【发布时间】:2021-09-12 08:04:37 【问题描述】:

我正在尝试基于 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>
  );

Here the sandbox

【问题讨论】:

我删除了我的答案,因为我注意到如果它不重新渲染,样式将始终为红色边框,因为焦点发生变化并重新评估,所以我不确定我的答案是否会无论如何申请。目的是什么? This thread might be helpful 【参考方案1】:

当 Provider 获得新值时,无法避免重新渲染。来自official docs on Context API:

只要 Provider 的 value prop 发生变化,所有作为 Provider 后代的消费者都会重新渲染。从 Provider 到其后代消费者(包括 .contextType 和 useContext)的传播不受 shouldComponentUpdate 方法的约束,因此即使祖先组件跳过更新,消费者也会更新。

【讨论】:

以上是关于如何使用自定义钩子和 useContext 仅渲染相关组件的主要内容,如果未能解决你的问题,请参考以下文章

React ,自定义钩子慢渲染

在使用数据库数据更新上下文后,React 'useContext' 钩子不会重新渲染

使用 React 钩子 useContext 避免不必要的重新渲染

使用新的 React 钩子 useContext 的正确方法是啥?

如何将 React 钩子(useContext、useEffect)与 Apollo 反应钩子(useQuery)结合起来

通过 useFooController/useFooHook 限制使用 useContext 的组件的渲染