Использование «Контекста» и «пользователя» в функциональном компоненте привело к повторному отображению всех дочерних компонентов

#javascript #reactjs #diff #memo

Вопрос:

React.js версия: v16.8.2

Использование контекста в сочетании с useReducer в функциональных компонентах, когда я изменил какое-то состояние, привело к тому, что все дочерние компоненты были обернуты контекстом.Поставщик», подлежащий повторному отображению. Что я должен сделать, чтобы предотвратить ненужную повторную визуализацию этих дочерних компонентов?

Теперь я использую «useMemo», чтобы обернуть структуру DOM всех дочерних компонентов.

Но что касается этого подхода, я хотел бы знать, влияет ли он на оптимизацию производительности? Потому что это может привести только к кэшированию структуры DOM, но ее собственные функциональные компоненты все равно будут повторно выполнены.

В то же время я также хочу знать, будет ли снова выполняться логика алгоритма diff для структуры DOM кэша «useMemo», который я использую?

Это мой код.

  1. Корневой компонент:
 import React, { useReducer } from 'react'
import * as styles from './home.scss'

import { Context } from './context.js'

import Component1 from './component1'
import Component2 from './component2'
import Component3 from './component3'

const Main = () => {
  const initState = {
    count: 0
  }

  const reducer = (state, action) => {
    switch (action.type) {
      case 'add':
        return {
          ...state,
          count: action.payload.count
        }
      default:
        return state
    }
  }

  const [state, dispatch] = useReducer(reducer, initState)

  return (
    <Context.Provider value={{ state, dispatch }}>
      <div className={styles.wrapper}>
        <Component1 />
        <Component2 />
        <Component3 />
      </div>
    </Context.Provider>
  )
}

export default Main
 
  1. Один из дочерних компонентов
 import React, { useContext, useCallback, useMemo } from 'react'
import * as styles from '../home.scss'

import { Context } from '../context.js'

const Component2 = () => {
  console.log(2)

  const { state, dispatch } = useContext(Context)

  const addClick = useCallback(() => {
    dispatch({
      type: 'add',
      payload: {
        count: 3
      }
    })
  }, [])

  return (
    useMemo(() => {
      return (
        <div className={styles.component2}>
          {console.log(2   '!')}
          <button onClick={addClick}>add</button>
          <div>{state.count}</div>
        </div>
      )
    }, [addClick, state.count])
  )
}

export default Component2
 
  1. Контекст
 import React from 'react'

export const Context = React.createContext({
  dispatch: () => {},
  state: {}
})
 

Ответ №1:

Разделиться Main на две части. Прямо сейчас, когда меняется редуктор, весь основной компонент также перерисовывается, включая все <ComponentN> . Но, если вы сделаете это:

 function MyProvider({ children }) {
  const initState = {
    count: 0
  }

  const reducer = (state, action) => {
    switch (action.type) {
      case 'add':
        return {
          ...state,
          count: action.payload.count
        }
      default:
        return state
    }
  }

  const [state, dispatch] = useReducer(reducer, initState)

  const value = React.useMemo(() => ({ state, dispatch }), [state, dispatch])

  return (
    <Context.Provider value={value}>
      {children}
    </Context.Provider>
  )
}
 

и ты меняешься Main , чтобы быть:

 const Main = () => {
  return (
    <MyProvider>
      <div className={styles.wrapper}>
        <Component1 />
        <Component2 />
        <Component3 />
      </div>
    </MyProvider>
  )
}
 

тогда у него все еще есть те же дочерние элементы, которые он получил от основного раньше, но React не будет повторно посещать это поддерево при повторном рендеринге.