繁体   English   中英

为什么 React.memo 不能与 useEffect 一起使用?

[英]Why is React.memo not working with useEffect?

我对反应有点陌生,所以我想要的是,我有一个切换按钮来切换人员组件,我有一个驾驶舱组件。 但是每当我切换persons 组件时,我不想总是重新渲染cockpit 组件。

所以这是我的 Cockpit.js 组件文件。

import React, { useEffect } from 'react';

import classes from './Cockpit.css';

const cockpit = props => {
  useEffect(() => {
    console.log('[Cockpit.js] useEffect');
    // Http request...
    setTimeout(() => {
      alert('Saved data to cloud!');
    }, 1000);
    return () => {
      console.log('[Cockpit.js] cleanup work in useEffect');
    };
  }, []);

  useEffect(() => {
    console.log('[Cockpit.js] 2nd useEffect');
    return () => {
      console.log('[Cockpit.js] cleanup work in 2nd useEffect');
    };
  });

  // useEffect();

  const assignedClasses = [];
  let btnClass = '';
  if (props.showPersons) {
    btnClass = classes.Red;
  }

  if (props.personsLength <= 2) {
    assignedClasses.push(classes.red); // classes = ['red']
  }
  if (props.personsLength <= 1) {
    assignedClasses.push(classes.bold); // classes = ['red', 'bold']
  }

  return (
    <div className={classes.Cockpit}>
      <h1>{props.title}</h1>
      <p className={assignedClasses.join(' ')}>This is really working!</p>
      <button className={btnClass} onClick={props.clicked}>
        Toggle Persons
      </button>
    </div>
  );
};

export default React.memo(cockpit);

这是我的 App.js

import React, { Component } from 'react';
import Persons from '../Components/Persons/Persons';
import classes from './App.css';
import Cockpit from '../Components/Cockpit/Cockpit'

class App extends Component {
  constructor(props) {
    super(props);
    console.log("[App.js] constructor");
  }

  state = {
    persons: [{id: "abc", name: "", age: 45}, 
    {id: "azz", name: "", age: 56},
    {id: "asq", name: "", age: 62}],
    showPersons: false,
    showCockpit: true
  }

  static getDerivedStateFromProps(props, state) {
    console.log("[App.js] getDerivedStateFromProps", props)
    return state; 
  }

  componentDidMount() {
    console.log('[App.js] componentDidMount')
  }

  shouldComponentUpdate(nextProps, nextState) {
    console.log('[App.js] shouldCompoentUpdate');
    return true;
  }

  componentDidUpdate() {
    console.log('[App.js] componentDidUpdate')
  }

  deletePersonHandler = (i) => {
    const persons = [...this.state.persons];
    persons.splice(i, 1);
    this.setState({persons: persons})
  }
  switchNameHandler = (newName) => {
    this.setState({persons: [{name: newName, age: 50}, {name: "Aysha", age: 56}, {name: "Momma", age: 62}]})
  }

  nameSwitchHandler = (event, id) => {
    const personIndex = this.state.persons.findIndex(p => {
      return p.id === id;
    })

    const person = {...this.state.persons[personIndex]}

    person.name = event.target.value;

    const persons = [...this.state.persons]
    persons[personIndex] = person;
    this.setState({persons: persons})
  }

  togglePersonHandler = () => {
    let doesChange = this.state.showPersons;
    this.setState({showPersons: !doesChange})
  }

  render() {

    console.log("[App.js] render");
    let person = null;

    if(this.state.showPersons) {
      person = (<Persons 
        persons={this.state.persons}
        clicked={this.deletePersonHandler}
        changed={this.nameSwitchHandler} />
      );
    }

    return (
      <div className={classes.App}> 
      <button onClick={() => this.setState({showCockpit: false})}>Remove Cockpit</button>
        {this.state.showCockpit ? (<Cockpit 
        title={this.props.appTitle}
        showPersons={this.state.showPersons}
        personsLength={this.state.persons.length}
        clicked={this.togglePersonHandler} />) : null}
        {person}
      </div>
    );
  }
}

export default App;

但即使当我切换它时,驾驶舱组件中的 useEffect 仍然控制台登录浏览器控制台而不应该这样做。 我似乎找不到我做错了什么。

正如您在这张图片中看到的那样,驾驶舱中的 useEffect 组件仍然在控制台中呈现......
浏览器控制台

单击 Toggle Persons,您将更改 App Component 中的 state。 这会导致重新渲染 App 和 Cockpit 组件。

 useEffect(() => {
    console.log('[Cockpit.js] 2nd useEffect');
    return () => {
      console.log('[Cockpit.js] cleanup work in 2nd useEffect');
    };
  });

上面的代码将触发每个渲染,因为您没有提供依赖项。 要解决此问题,您需要向上述代码添加依赖项。

默认情况下, React.memo将对props object 进行浅层相等比较。 这意味着它将检查道具中的每个顶级项目是否相等,如果其中任何一个发生更改,它将重新渲染。

当您单击 people 切换按钮时,它将更改App组件中的showPersons ,这也是您传递给<Cockpit>的道具。 因此,即使使用React.memo也会重新渲染。 如果它不会重新渲染,它就不会正确更新您的 Button class 添加或删除classes.Red因为这取决于showPersons道具。

它与cockpit内的useEffect无关,它只会在重新渲染后被调用,但不会导致它首先重新渲染。

由于showPersons发生变化,它会将其检测为已更改的道具。

你可以在 React.memo 中添加一个相等的React.memo来告诉 react 什么时候考虑 memoization stale:

// Will only rerender when someValue changes
export default React.memo(Cockpit, (oldProps, newProps) => oldProps.someValue === newProps.someValue) 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM