useMemo, useCallback и меморандум об хуке реакции

React.js

Примечание: хуки можно использовать только в функциях (компоненты без состояния).

Использование useMome и useCallback аналогично, они будут выполняться при первом рендеринге, а затем выполняться снова, когда переменные, от которых они зависят, изменяются, и оба хука возвращают кешированное значение, useMemo возвращает кешированную переменную, а useCallback возвращает кеш. функция.

const value = useMemo(fnM, [a]);

const fnA = useCallback(fnB, [a]);

1. Применение памятки

React.memo — это компонент более высокого порядка. Он очень похож на React.PureComponent.

По умолчанию он будет выполнять поверхностное сравнение только сложных объектов. Если вы хотите контролировать процесс сравнения, передайте пользовательскую функцию сравнения через второй параметр. Это противоположно возвращаемому значению метода shouldComponentUpdate.

function MyComponent(props) {
  /* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
  /*
  如果把 nextProps 传入 render 方法的返回结果与
  将 prevProps 传入 render 方法的返回结果一致则返回 true,
  否则返回 false
  */
}
export default React.memo(MyComponent, areEqual);

Когда родительский компонент вводит дочерний компонент, это часто приводит к ненужным потерям между компонентами.Давайте возьмем пример, чтобы понять сцену.

const Child = (props) => {
    console.log('子组件?')
    return(
        <div>我是一个子组件</div>
    );
}
const Page = (props) => {
    const [count, setCount] = useState(0);
    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>加1</button>
            <p>count:{count}</p>
            <Child />
        </>
    )
}

Каждый раз, когда происходит подсчет обновлений родительского компонента, дочерний компонент будет обновляться. В следующей версии используется памятка, а подкомпонент изменения счетчика не обновляется.

const Child = (props) => {
    console.log('子组件?')
    return(
        <div>我是一个子组件</div>
    );
}
const ChildMemo = memo(Child);

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

    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>加1</button>
            <p>count:{count}</p>
            <ChildMemo />
        </>
    )
}

2. Используйте обратный вызов

Когда родительский компонент передает состояние дочернему компоненту, заметка, кажется, не имеет никакого эффекта, и дочерний компонент все еще выполняется.На данный момент мы представим два хука useCallback и useMemo из хуков.

//子组件会有不必要渲染的例子
interface ChildProps {
    name: string;
    onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
    console.log('子组件?')
    return(
        <>
            <div>我是一个子组件,父级传过来的数据:{name}</div>
            <button onClick={onClick.bind(null, '新的子组件name')}>改变name</button>
        </>
    );
}
const ChildMemo = memo(Child);

const Page = (props) => {
    const [count, setCount] = useState(0);
    const [name, setName] = useState('Child组件');

    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>加1</button>
            <p>count:{count}</p>
            <ChildMemo name={name} onClick={(newName: string) => setName(newName)}/>
        </>
    )
}

На основе приведенного выше кода, когда родитель вызывает дочерний, добавьте useCallback в параметр onClick, а параметр равен [], после первой инициализации он не изменится.

//子组件没有必要渲染的例子
interface ChildProps {
    name: string;
    onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
    console.log('子组件?')
    return(
        <>
            <div>我是一个子组件,父级传过来的数据:{name}</div>
            <button onClick={onClick.bind(null, '新的子组件name')}>改变name</button>
        </>
    );
}
const ChildMemo = memo(Child);

const Page = (props) => {
    const [count, setCount] = useState(0);
    const [name, setName] = useState('Child组件');
    
    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>加1</button>
            <p>count:{count}</p>
            <ChildMemo name={name} onClick={ useCallback((newName: string) => setName(newName), []) }/>
            {/* useCallback((newName: string) => setName(newName),[]) */}
            {/* 这里使用了useCallback优化了传递给子组件的函数,只初始化一次这个函数,下次不产生新的函数
        </>
    )
}

3. Используйте юзмемо

//子组件会有不必要渲染的例子
interface ChildProps {
    name: { name: string; color: string };
    onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
    console.log('子组件?')
    return(
        <>
            <div style={{ color: name.color }}>我是一个子组件,父级传过来的数据:{name.name}</div>
            <button onClick={onClick.bind(null, '新的子组件name')}>改变name</button>
        </>
    );
}
const ChildMemo = memo(Child);

const Page = (props) => {
    const [count, setCount] = useState(0);
    const [name, setName] = useState('Child组件');
    
    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>加1</button>
            <p>count:{count}</p>
            <ChildMemo 
                name={{ name, color: name.indexOf('name') !== -1 ? 'red' : 'green'}} 
                onClick={ useCallback((newName: string) => setName(newName), []) }
            />
        </>
    )
}

Обновите имя атрибута до типа объекта. В это время дочерний компонент по-прежнему выполняется так же. Когда родительский компонент обновляет другие состояния, атрибут имени объекта дочернего компонента всегда будет перерисовываться и изменяться, что приводит к непрерывному исполнение, что тоже не нужно.

Чтобы решить эту проблему, используйте useMemo с параметром name для обновления в зависимости от изменения данных State.name.

interface ChildProps {
    name: { name: string; color: string };
    onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
    console.log('子组件?')
    return(
        <>
            <div style={{ color: name.color }}>我是一个子组件,父级传过来的数据:{name.name}</div>
            <button onClick={onClick.bind(null, '新的子组件name')}>改变name</button>
        </>
    );
}
const ChildMemo = memo(Child);

const Page = (props) => {
    const [count, setCount] = useState(0);
    const [name, setName] = useState('Child组件');
    
    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>加1</button>
            <p>count:{count}</p>
            <ChildMemo 
                //使用useMemo,返回一个和原本一样的对象,第二个参数是依赖性,当name发生改变的时候,才产生一个新的对象
                name={
                    useMemo(()=>({ 
                        name, 
                        color: name.indexOf('name') !== -1 ? 'red' : 'green'
                    }), [name])
                } 
                onClick={ useCallback((newName: string) => setName(newName), []) }
                {/* useCallback((newName: string) => setName(newName),[]) */}
                {/* 这里使用了useCallback优化了传递给子组件的函数,只初始化一次这个函数,下次不产生新的函数
            />
        </>
    )
}

4. Резюме

В случае, когда для значения функции не требуется родительский элемент подсборки, необходимо использовать только функцию запоминания, может быть обернут подсборка. В случае использования функции нет необходимости рассматривать функцию передачи в подсборку с помощью useCallback. Варьируется в зависимости от значения элементов, а также когда используется объект и эквивалентный массив useMemo (когда возвращаемым типом являются исходные данные, такие как строки, числа, логические значения, не используйте useMemo). Не используйте эти крючки вслепую.

Ссылка на ссылку:

Лучшие практики React Hook

React.memo

Реагировать на расширенное использование и личное использование хуков (версия Typescript) — оптимизация производительности 3.useCallback+useMemo+memo