Callbacks

componentDidMount - good to use for event subscribtion and server API calls:

class VirtualList extends React.Component {
    state = { offsetY: 0 }
    componentDidMount() {
        document.addEventListener(
            'scroll', 
            ()=>this.setState({ offsetY: window.pageYOffset })
        )
    }
    // ...
} 
class Stories extends React.Component {
    state = { 
        stories: [],
        loading: true,
        hasError: false
    }

    componentDidMount() {
        fetch('/stories/all')
            .then(res => res.json())
            .then(data => this.setState({ stories: data.stories, loading: false }))
            .catch(e => this.setState({ ...this.state, loading: false, hasError: true }))
    }
    // ...
} 

componentDidUpdate - goot to use with server API calls if comparisons with previous state or props are needed:

class Product extends React.Component {
    state = { 
        productData: null,
        loading: true,
        hasError: false
    }
    
    getProductData = () => {
        fetch(`/api/v1/products/${this.props.productId}`)
            .then(res => res.json())
            .then(data => this.setState({ productData: data.productData, loading: false }))
            .catch(e => this.setState({ ...this.state, loading: false, hasError: true }))
    }    

    componentDidMount() {
        this.getProductData()
    }

    componentDidUpdate(prevProps, prevState) {
      // Сравниваем предыдущие пропсы с обновившимися.
        // Если они отличаются, то делаем запрос:
      if (this.props.productId !== prevProps.productId) {
        this.getProductData();
      }
    }
    // ...
} 

setState() can be used in componentDidUpdate but a condition should be used, otherwise there's a risk of an infinite loop:

// ...
componentDidUpdate(prevProps, prevState) {
  // Сравниваем предыдущее состояние с новыми
    // Если нужные нам ключи отличаются, то делаем запрос:
  if (this.props.productId !== prevProps.productId) {
        this.setState({ ...this.state, loading: true });
    this.getProductData();
  }
}
// ... 

shouldComponentUpdate - used for optimization to override the default rules for component re-rendering upon props or state change:

class TodoItem extends React.Component {
  constructor(props) {
    super(props);
    this.state = { done: false };
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.tagColor !== nextProps.tagColor) {
      // Если изменилось значение props.tagColor, то будет вызван повторный рендер
      return true;
    }
    if (this.state.done !== nextState.done) {
      // Если изменилось значение state.done, то будет вызван повторный рендер
      return true;
    }

    // Во всех остальных случаях повторного рендеринга не будет
    return false;
  }

  toggleTodo = () => {
    this.setState({ done: !this.state.done });
  };

  render() {
    const btnText = this.state.done ? "Вернуть в работу" : "Выполнено";
    return (
      <>
        <TodoTag tagColor={this.props.tagColor} />
        <button onClick={this.toggleTodo}>{btnText}</button>
      </>
    );
  }
}

componentWillUnmount - helpful for event handler cleanup to prevent memory leaks:

class VirtualList extends React.Component {
  state = { offsetY: 0 };
  // Создадим отдельную функцию для того чтобы слушатель событий можно было удалить
  setOffset = () => {
    this.setState({ offsetY: window.pageYOffset });
  };

  componentDidMount() {
    document.addEventListener("scroll", this.setOffset);
  }

  componentWillUnmount() {
    // Не забывайте отписываться от событий, чтобы не допустить утечек памяти
    document.removeEventListener("scroll", this.setOffset);
  }
  // ...
} 

#react