Reactjs 是否应在功能组件中更新组件

Reactjs 是否应在功能组件中更新组件,reactjs,Reactjs,我有一个关于React的shouldComponentUpdate(未被覆盖时)的问题。我确实更喜欢纯功能组件,但我担心它每次都会更新,即使道具/状态没有改变。 所以我考虑改用PureComponent类 我的问题是: 功能组件是否具有与PureComponents相同的shouldComponentUpdate检查?还是每次都会更新?在React中,功能组件是无状态的,并且没有生命周期方法无状态组件是一种优雅的编写React组件的方法,我们的包中没有太多代码。但是在内部,无状态组件被包装在一个

我有一个关于React的
shouldComponentUpdate
(未被覆盖时)的问题。我确实更喜欢纯功能组件,但我担心它每次都会更新,即使道具/状态没有改变。 所以我考虑改用PureComponent类

我的问题是:
功能组件是否具有与PureComponents相同的
shouldComponentUpdate
检查?还是每次都会更新?

在React中,功能组件是无状态的,并且没有生命周期方法无状态组件是一种优雅的编写React组件的方法,我们的包中没有太多代码。但是在内部,无状态组件被包装在一个类中,当前没有应用任何优化。这意味着无状态和有状态组件在内部具有相同的代码路径(尽管我们对它们的定义不同)

但在未来,React可能会优化无状态组件,如下所述:

将来,我们还可以进行性能优化 通过避免不必要的检查和内存来指定这些组件 拨款

shouldComponentUpdate
这就是我们可以应用自定义优化并避免不必要的组件重新渲染的地方。此方法在不同类型组件中的使用说明如下:

  • 功能性无状态组件

    如前所述,无状态组件没有生命周期方法,因此我们无法使用
    shouldComponentUpdate
    优化它们。但它们已经以不同的方式进行了优化,它们的代码结构更简单、更优雅,并且比具有所有生命周期挂钩的组件的字节数更少

  • 扩展React.PureComponent

    React v15.3.0,我们有了一个新的基类,名为
    PureComponent
    使用内置的PureRenderMixin扩展。在引擎盖下,这使用了一个
    shouldComponentUpdate
    中当前道具/状态与下一个道具/状态的粗略比较

    也就是说,我们仍然不能依靠
    PureComponent
    类将组件优化到我们想要的水平。如果我们有
    Object
    类型(数组、日期、普通对象)的道具,就会发生这种异常情况。这是因为我们在比较对象时遇到了这个问题:

    const obj1 = { id: 1 };
    const obj2 = { id: 1 };
    console.log(obj1 === obj2); // prints false
    
    因此,一个肤浅的比较不足以确定事情是否发生了变化。但是如果你的道具是字符串、数字、布尔值,那么就使用
    PureComponent
    class。。而不是物体。如果您不想实现自己的自定义优化,也可以使用它

  • 扩展反应组件

    考虑上面的例子;如果我们知道如果
    id
    发生了变化,那么我们可以通过比较
    obj1.id===obj2.id
    来实现我们自己的定制优化。在这里,我们可以
    扩展
    我们的普通
    组件
    基类,并使用
    shouldComponentUpdate
    自己对特定键进行比较


无论道具是否更改,功能组件将在每次父级渲染时重新渲染

但是,使用
React.memo
高阶组件,功能组件实际上可以获得相同的
shouldComponentUpdate
检查,该检查用于
PureComponent

您只需在导出时将功能组件包装在
React.memo
中,如下所示

所以

现场示例(也包括):

console.log(“HI”);
常量父项=(道具)=>{
const[ticks,setTicks]=React.useState(0);
const tickRef=React.useRef();
tickRef.current=滴答声;
const[clicks,setClicks]=React.useState(0);
setTimeout(()=>setTicks(ticks+1),500);
返回(
在勾号{tickRef.current}处通过单击{clicks}呈现父级。
设置单击次数(单击次数+1)}>
添加额外的点击
);
};
const Child=({tickRef,clicks})=>(
通过单击{clicks}在勾号{tickRef.current}处呈现的子级

); const MemoChild=React.memo(Child); ReactDOM.render( , document.getElementById('root')) );
另一种方法是仅在更新监视的值时使用更新值:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
在对象的情况下,有一个选项可以使用状态钩子来缓存感兴趣的变量的值,在确保它被更新之后。例如使用
lodash

const [fooCached, setFooCached]: any = useState(null);

if (!_.isEqual(fooCached, foo)) {
  setFooCached(foo);
}). 

const showFoo = useMemo(() => {
    return <div>Foo name: { foo.name }</div>
}, [fooCached]);
const[fooCached,setFooCached]:any=useState(null);
如果(!.isEqual(fooCached,foo)){
setFooCached(foo);
}). 
const showFoo=useMemo(()=>{
返回Foo名称:{Foo.name}
},[fooched]);

每当父级发生更改时更新?TLDR:那么,可以肯定地说,使用无状态组件几乎总是比实现shouldComponentUpdate的常规组件更有效吗?请看这个。可能值得注意的是,功能组件不再一定是无状态的,多亏了。是的,这个问题是两年前的,但我喜欢今天的答案。我相信任何使用DEP的钩子都能做到这一点,而不仅仅是使用备忘录。使用相同语法的useEffect:
useEffect(()=>computeExpensiveValue(a,b),[a,b])
对这个好答案的进一步解释:在父组件的整个“生命周期”(这就是
useRef
的工作方式)中,
tickRef
是同一个对象,只有
current
属性被更改。诀窍是,
memo
只对道具进行了肤浅的比较,因此它不知道
tickRef
有一个不断变化的属性。因此可以肯定地说,道具的深层变化也不会触发子组件中的重新渲染。这是诱使组件不重新渲染的一种方法。也很简单。
const Parent = ( props ) => {
  // Ticks is just some state we update 2 times every second to force a parent rerender
  const [ticks, setTicks] = React.useState(0);
  setTimeout(() => setTicks(ticks + 1), 500);
  // The ref allow us to pass down the updated tick without changing the prop (and forcing a rerender)
  const tickRef = React.useRef();
  tickRef.current = ticks;

  // This is the prop children are interested in
  const [clicks, setClicks] = React.useState(0);

  return (
    <div>
      <h2>Parent Rendered at tick {tickRef.current} with clicks {clicks}.</h2>
      <button 
        onClick={() => setClicks(clicks + 1)}>
        Add extra click
      </button>
      <Child tickRef={tickRef} clicks={clicks}/>
      <MemoChild tickRef={tickRef} clicks={clicks}/>
    </div>
  );
};

const Child = ({ tickRef, clicks }) => (
  <p>Child Rendered at tick {tickRef.current} with clicks {clicks}.</p>
);

const MemoChild = React.memo(Child);
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const [fooCached, setFooCached]: any = useState(null);

if (!_.isEqual(fooCached, foo)) {
  setFooCached(foo);
}). 

const showFoo = useMemo(() => {
    return <div>Foo name: { foo.name }</div>
}, [fooCached]);