如何使用useContext防止Component中不必要的渲染?

时间:2020-05-29 15:54:11

标签: javascript reactjs

我想知道如何避免对没有收到component但使用props的孩子useContext进行不必要的更新。

我已将React.memo添加到Child2 component中,以便进行一些演示测试。

如您所见,当我更改input值(使用context将数据传递到child1时,Child2不会重新呈现,因为{{ 1}}可以防止这种行为。

如何在Child1中也防止不必要的渲染?我知道React.memo不能按需使用React.memo,并且使用props不会发生,因为没有context会传递给此props < / p>

component

App.js

import React, {useState} from 'react'; import './App.css'; import Child1 from "./Child1"; import Child2 from "./Child2"; import SillyContext from './context' function App() { const [name, setName] = useState('Radha'); const [count, setCount] = useState(0) const increaseCount = () => { setCount(prevState => prevState + 1) } return ( <div className="App"> <Child2 count={count}/> <button onClick={increaseCount}>Increase</button> <input type="text" value={name} onChange={(event => setName(event.target.value))}/> <SillyContext.Provider value={{name}}> <Child1/> </SillyContext.Provider> </div> ); } export default App;

Child1

import React, {useContext} from "react"; import SillyContext from './context' export default function Child1() { const sillyContext = useContext(SillyContext) console.log('[Child 1 ran]') return ( <div> <div>[Child 1]: {sillyContext.name}</div> </div> ) }

Child2

2 个答案:

答案 0 :(得分:1)

更新计数时,Child1会重新渲染的主要问题是因为您每次都将新的对象引用传递给Context Provider。

如果App组件重新渲染,则在其中渲染的所有元素都将重新渲染,除非它们实现了备注或为PureComponent或使用shouldComponentUpdate

您可以进行2项更改来修复您的重新渲染

  • 用React.memo包装Child1
  • 使用useMemo记住作为值传递给提供者的对象

App.js

function App() {
  const [name, setName] = useState("Radha");
  const [count, setCount] = useState(0);

  const increaseCount = () => {
    setCount(prevState => prevState + 1);
  };

  const value = useMemo(() => ({ name }), [name]);
  return (
    <div className="App">
      <Child2 count={count} />
      <button onClick={increaseCount}>Increase</button>

      <input
        type="text"
        value={name}
        onChange={event => setName(event.target.value)}
      />
      <SillyContext.Provider value={value}>
        <Child1 />
      </SillyContext.Provider>
    </div>
  );
}

Child2

const Child2 = React.memo(function(props) {
  console.log("[Child2 Ran!]");
  return <div>[Child2] - Count: {props.count}</div>;
});

孩子1

const Child1 = React.memo(function() {
  const sillyContext = useContext(SillyContext);
  console.log("[Child 1 ran]");
  return (
    <div>
      <div>[Child 1]: {sillyContext.name}</div>
    </div>
  );
});

Working demo

答案 1 :(得分:0)

您可以在Child1组件内使用useMemo。它不能解决重新渲染问题,但可以改进。

Following snippet is third variant proposed by React core dev

我们可以使我们的代码更加冗长,但将其保留在 通过在useMemo中包装返回值并指定单个组件 它的依赖关系。我们的组件仍然可以重新执行,但是React 如果所有useMemo输入都相同,则不会重新渲染子树。

const { useState, useEffect, createContext, useContext, useMemo } = React;

const SillyContext = createContext();

const Child2 = React.memo(({count}) => {
    return <div>Count: {count}</div>
})         

const Child1 = () => {
    const {name} = useContext(SillyContext);
    
    return useMemo(() => {
    console.log('Child1');
      return <div>
        <div>Child 1: {name}</div>
      </div>}, [name])
}

const App = () => {
  const [count, setCount] = useState(0);

  const increaseCount = () => {
        setCount(prevState => prevState + 1)
  }

return <div>
  <Child2 count={count}/>
   <button onClick={increaseCount}>Increase</button>
  <Child1/>
</div>
}

ReactDOM.render(
    <SillyContext.Provider value={{name: 'test'}}>
      <App />
    </SillyContext.Provider>,
    document.getElementById('root')
  );
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>