Интервьюер умоляет вас перестать спрашивать меня о крючках

React.js
Интервьюер умоляет вас перестать спрашивать меня о крючках

Введение

Позвольте сначала задать вам несколько вопросов. Это все вопросы, которые мне задавали в интервью. Если они верны, я не смогу их исправить. …

  • ПисатьhooksПо сравнению с написанием компонентов класса,hooksКаковы преимущества?
  • Как мы инкапсулируемhook?
  • hooksКаков принцип?

Хоть интервью и холодное, учеба должна продолжаться😭

Два углубленных понимания хуков

useState

  • использовать

useStateИспользование очень просто🙉, после предложения возвращает массив, значением массива является текущийstateи обновитьstateФункция;useStateПараметрыпеременная, объект или функция, переменная или объект будут использоваться какstateНачальное значение , если это функция, возвращаемое значение функции будет использоваться как начальное значение.

  • Массовое обновление

Посмотрите на код ниже

function Count(){
    let [count,setCount] = useState(0)
    const handleAdd = function(){
        setCount(count+1)
        setCount(count+1)
    }
    return(
        <div>
            <p>{count}</p>    
            /*每次点击加1*/
            <button onClick={handleAdd}>加一</button>
        </div>
    )
}

В одном и том же событии он не будет вызываться дваждыsetCountи разрешиcountУвеличить в два раза, представьте, если в одном и том же событии каждый звонокsetCountэффективны, то каждый вызовsetCountКомпонент будет перерисовываться один раз, что несомненно влияет на производительность, ведь если измененныйstateтакой же, последнийsetCountновый в функцииstateперезапишет предыдущий

useEffect && useLayoutEffect

  • использовать

эти двоеhookИспользование такое же, первый параметр — функция обратного вызова, второй параметр — массив, а содержимое массива — зависимостьdeps, функция обратного вызова выполняется после изменения зависимостей, обратите внимание, что компонент по умолчанию будет выполняться один раз при каждом его рендеринге.Если второй параметр не передан, пока компонентstateИзменение вызовет функцию обратного вызова.Если передан пустой массив, он будет выполнен только один раз во время инициализации. Кроме того, если вы используетеreturnВозвращает функцию, которая будет выполняться перед вызовом функции обратного вызова при каждом повторном рендеринге компонента.

  • разница

На поверхности эти дваhookОтличие в том, что время выполнения разное,useEffectФункция обратного вызова будет выполнена после рендеринга страницы;useLayoutEffectбудет выполнен до того, как страница будет отображена. в действительностиReactэтим двумhookобрабатываются по-разному,useEffectявляется асинхронным вызовом, аuseLayoutEffectявляется синхронным вызовом.
когда использоватьuseEffect, когда использоватьuseLayoutEffectШерстяная ткань?
Я так понимаю, что это зависит от ситуации Если функция обратного вызова изменитstateвызывает повторный рендеринг компонента, что можетuseLayoutEffect, потому что в это время используйтеuseEffectМожет вызвать мерцание страницы; Если данные запрашиваются в функции обратного вызова или время выполнения js слишком велико, рекомендуется использоватьuseEffect; потому что в это время используйтеuseLayoutEffectБлокирует рендеринг браузера.

useMemo && useCallback

эти двоеhookМожет использоваться для оптимизации производительности и сокращения повторного рендеринга компонентов; теперь давайте взглянем на эти две магии.hookкак пользоваться.

  • uesMemo
function MemoDemo() {
    let [count, setCount] = useState(0);
    let [render,setRender] = useState(false)
    const handleAdd = () => {
        setCount(count + 1);
    };
    const Childone = () => {
        console.log("子组件一被重新渲染");
        return <p>子组件一</p>;
    };
    const Childtwo = (props) => {
        return (
            <div>
                <p>子组件二</p>
                <p>count的值为:{props.count}</p>
            </div>
        );
    };
    const handleRender = ()=>{
        setRender(true)
    }
    return (
        <div style={{display:"flex",justifyContent:'center',alignItems:'center',height:'100vh',flexDirection:'column'}}>
            {
                useMemo(() => {
                    return <Childone />
                }, [render])
            }
            <Childtwo count={count} />
            <button onClick={handleAdd}>增加</button>
            <br/>
            <button onClick={handleRender} >子组件一渲染</button>
        </div>
    );
}

Childoneтолько компонентыrenderИзменения будут повторно отображены

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

const Childone = React.memo((props) => {
    console.log("子组件一被重新渲染",props);
    return <p>子组件一{props.num}</p>;
})
function MemoDemo() {
    let [count, setCount] = useState(0);
    let [render,setRender] = useState(false)
    let [num,setNum] = useState(2)
    const handleAdd = () => {
        setCount(count + 1);
    };
   
    const Childtwo = (props) => {
        return (
            <div>
                <p>子组件二</p>
                <p>count的值为:{props.count}</p>
            </div>
        );
    };
    const handleRender = ()=>{
        setRender(true)
    }
    return (
        <div style={{display:"flex",justifyContent:'center',alignItems:'center',height:'100vh',flexDirection:'column'}}>
            {/* {
                useMemo(() => {
                    return <Childone />
                }, [render])
            } */}
            <Childone num={num}/>
            <Childtwo count={count} />
            <button onClick={handleAdd}>增加</button>
            <br/>
            <button onClick={handleRender} >子组件一渲染</button>
        </div>
    );
}

Этот пример является комбинацией предыдущего примераChildoneВыньте его и наденьтеReact.memoВ результате компонент не будет повторно визуализироваться после нажатия кнопки «Добавить», поскольку компонент не будет повторно визуализироваться.numбез изменений

  • useCallback

По-прежнему в приведенном выше примере мы помещаемhandleRenderиспользоватьuseCallbackпакет, то есть здесьnumЕсли изменений нет, каждый раз будет передаваться одна и та же функция, если она здесь не используетсяuseCallbackпакеты, каждый раз генерируются новыеhandleRender, Привести кReact.memoв функцииpropsПосле неглубокого сравнения было обнаружено, что для запуска рендеринга была сгенерирована новая функция.

const Childone = React.memo((props) => {
    console.log("子组件一被重新渲染",props);
    return <p>子组件一{props.num}</p>;
})
export default function MemoDemo() {
    let [count, setCount] = useState(0);
    let [render,setRender] = useState(false)
    let [num,setNum] = useState(2)
    const handleAdd = () => {
        setCount(count + 1);
    };
   
    const Childtwo = (props) => {
        return (
            <div>
                <p>子组件二</p>
                <p>count的值为:{props.count}</p>
            </div>
        );
    };
    const handleRender = useCallback(()=>{
        setRender(true)
    },[num])
    return (
        <div style={{display:"flex",justifyContent:'center',alignItems:'center',height:'100vh',flexDirection:'column'}}>
            {/* {
                useMemo(() => {
                    return <Childone />
                }, [render])
            } */}
            <Childone num={num} onClick={handleRender}/>
            <Childtwo count={count} />
            <button onClick={handleAdd}>增加</button>
            <br/>
            <button onClick={handleRender} >子组件一渲染</button>
        </div>
    );
}

useRef

этоhookОбычно используется для получения экземпляров компонентов, а также может использоваться для кэширования данных❗ Чтобы получить экземпляр, не нужно слишком много объяснять.Следует отметить, что экземпляры есть только у компонентов класса;
СконцентрируйсяuseRefКак кэшировать данные:

function UseRef() {
    let [data, setData] = useState(0)
    let initData = {
        name: 'lisa',
        age: '20'
    }
    let refData = useRef(initData)   //refData声明后组件再次渲染不会再重新赋初始值
    console.log(refData.current);
    refData.current = {       //修改refData后页面不会重新渲染
        name: 'liyang ',
        age: '18'
    }
    const reRender = () => {
        setData(data + 1)
    }
    return (
        <div>
            <button onClick={reRender}>点击重新渲染组件</button>
        </div>
    )
}

После повторного рендеринга компонента переменная будет переназначена, вы можете использоватьuseRefКэшировать данные.После изменения этих данных компонент не будет перерисовываться.Если вы используетеuseStateСохраните данные.После изменения данных компонент будет перерендерен, поэтому мы хотим сохранить данные по-тихому,useRefЭто лучший выбор 👊

Три нестандартных крючка

настроитьhook, это,hookупаковка, почему упаковкаhookШерстяная ткань?reactОфициальный сайт дает ответ

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

Давайте посмотрим на этот пример:

export default function CustomHook() {
    let refone = useRef(null)
    let X, Y, isMove = false,left,top
    //基于鼠标事件实现拖拽
    useEffect(() => {
        const dom = refone.current
        dom.onmousedown = function (e) {
            isMove = true
            X = e.clientX - dom.offsetLeft;
            Y = e.clientY - dom.offsetTop;
        }
        dom.onmousemove = function (e) {
            if (isMove) {
                left = e.clientX - X
                top = e.clientY - Y
                dom.style.top = top + "px"
                dom.style.left = left + "px"
            }

        }
        dom.onmouseup = function (e) {
            isMove = false
        }
    }, [])
    return (
        <div style={{ display: "flex", justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
            <div ref={refone} style={{ width: '70px', height: '70px', backgroundColor: '#2C6CF9',position:'absolute' }}></div>
        </div>
    )
}

Мы просто используем события мыши, чтобы добиться эффекта перетаскивания квадрата, что, если этот эффект требуется и на других страницах? 😏 Итак, мы можем рассмотреть возможность инкапсуляции той же логики, как если бы мы извлекали общедоступные компоненты. Рассмотрим следующий пример:

import {useEffect, useRef } from "react";
function useDrop() {
    let refone = useRef(null)
    let X, Y, isMove = false,left,top
    //基于鼠标事件实现拖拽
    useEffect(() => {
        const dom = refone.current
        dom.onmousedown = function (e) {
            isMove = true
            X = e.clientX - dom.offsetLeft;
            Y = e.clientY - dom.offsetTop;
        }
        dom.onmousemove = function (e) {
            if (isMove) {
                left = e.clientX - X
                top = e.clientY - Y
                dom.style.top = top + "px"
                dom.style.left = left + "px"
            }

        }
        dom.onmouseup = function (e) {
            isMove = false
        }
    }, [])
    return refone
}
export default function CustomHook() {
    let refone = useDrop()
    let reftwo = useDrop()
    return (
        <div style={{ display: "flex", justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
            <div ref={refone} style={{ width: '70px', height: '70px', backgroundColor: '#2C6CF9',position:'absolute' }}></div>
            <div ref={reftwo} style={{ width: '70px', height: '70px', backgroundColor: 'red',position:'absolute' }}></div>
        </div>
    )
    }

Чтобы уменьшить объем кода, мы показываем только использование инкапсулированного кода на той же странице.hook, на самом деле инкапсулирует этоhookРазве не удивительно, что мы изменили всего несколько строк кода и реализовали повторное использование логики! 😆 Обратите внимание, чтоhookФункция-оболочка должна начинаться сuseв начале, потому что с помощьюhookСуществуют правила сами по себе, например, нельзя использовать в условных операторах.hook, нельзя использовать вне функцииhook; если не применимоuseначать инкапсуляциюhook,ноreactНевозможно автоматически проверить использование этой функции.hookсоответствовать правилам.

Четыре резюме

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