美文网首页
hook注意事项

hook注意事项

作者: skoll | 来源:发表于2020-07-17 15:19 被阅读0次

    每次渲染,都会走到hook里面

    1 .这个里面好像是因为count值会变化,外面引用到了,所以每次都会重新渲染useCounter这个函数
    2 .但是这个里面的useEffect其实只会调用一次

    const count=useCounter()
    //别的地方调用了这个hook
    
    function useCounter(){
        console.log('调用了count')
    //这里是每次都打印出来的
        const [count,setCount]=useState(0)
    
        // useEffect(()=>{
        //     setInterval(() => {
        //         console.log('useCounter',count)
        //         setCount(count + 1);
        //     }, 1000);      
        // },[])
        // 问题1:这里的count取的是初始执行这个useEffect时候的值,也就是0,所以每次加计算的都是从0开始,所以这里的值应该会一直是0
        
        // useEffect(()=>{
        //     let id=setInterval(() => {
        //         console.log('useCounter',count)
        //         setCount(count + 1);
        //     }, 1000);
        // },[count])
        // 问题2:每次重新执行,setInterval都是会重新定义,所以需要一个全局变量来保存这个东西,比如用一个ref来保存,或者使用函数模式设置count
    
        useEffect(()=>{
            let id=setInterval(() => {
                console.log('useCounter',count)
                setCount(c=>c+1);
            }, 1000);
            // 不需要传入依赖,只执行一次,setInterval不会重置,setCount的函数设置方式,可以实现我们的效果
        },[])
    
        // 或者全局挂在一个ref上面
    
        return [count]
    
    }
    

    Hook的每次调用都有一个独立的state被创建,复用的是逻辑状态,而不是state值

    count仅是一个数字而已。它不是神奇的“data binding”, “watcher”, “proxy”

    1 .react换了一种模型

    const count = 42;
    // ...
    <p>You clicked {count} times</p>
    

    2 .这个好像和我的想法一样
    3 .第一次渲染的时候,拿到的count是1,第二次调用setCount,这个值变化,react再次渲染,这次count是1
    4 .每一次更新状态,react会重新渲染组件,每一次渲染都会拿到独立的count,这个状态值是函数中的一个常量
    5 .当我们调用setCount的时候,react会带着一个不同的count值在次调用这个组件,然后react会更新dom,保证输出和渲染一致

    每一次渲染都有自己的事件处理函数,包括传入的参数

    1 .我们的组件函数每次渲染都会被调用,每次调用的count都是常量,并且他被赋予了当前渲染中的状态值,所以才会有如果函数依赖外面的参数,就要把参数传入到第二个参数里面,当参数发生变化就让这个函数重新创建一遍
    2 .每一次渲染都有一个“新版本”的handleClick,每一个版本的handleClick都记住了自己的event
    3 .vue更像是把所有数据都存在一个地方,每次用到参数的时候都去那里找当前最新的,这个函数一旦定义好就不变了
    4 .再任意一次的渲染中,props和state始终是不变的,props和state再不同的渲染中是相互独立的,所以使用到他们的任意值也都是相互独立的,他们都属于一次特定的渲染。即使事件处理中的异步函数调用“看到”的也是这次渲染中的值。也有可能不是最新的值

    effect:每次渲染都有他自己的effects

    1 .并不是count值在“不变”的effect中发生了改变,而是effect函数本身在每一次渲染中都不同,如果effect依赖于外部的count值,那么每一个版本的effect应该都是根据最新的count来创建的
    2 .虽然effect是一个,但是每次渲染都是一个不同的函数,并且每个effect函数得到的props,或者state都来自属于他的那次特定渲染
    3 .effect渲染的时间

    function Counter() {
      // ...
      useEffect(
        // Effect function from first render
        () => {
          document.title = `You clicked ${0} times`;
        }
      );
      // ...
    }
    React: 给我状态为 0时候的UI。
    你的组件:
    
    给你需要渲染的内容: <p>You clicked 0 times</p>。
    记得在渲染完了之后调用这个effect: () => { document.title = 'You clicked 0 times' }。
    React: 没问题。开始更新UI,喂浏览器,我要给DOM添加一些东西。
    浏览器: 酷,我已经把它绘制到屏幕上了。
    React: 好的, 我现在开始运行给我的effect
    
    运行 () => { document.title = 'You clicked 0 times' }。
    
    第一次渲染
    
    // After a click, our function is called again
    function Counter() {
      // ...
      useEffect(
        // Effect function from second render
        () => {
          document.title = `You clicked ${1} times`;
        }
      );
      // ...
    }
    你的组件: 喂 React, 把我的状态设置为1。
    React: 给我状态为 1时候的UI。
    你的组件:
    
    给你需要渲染的内容: <p>You clicked 1 times</p>。
    记得在渲染完了之后调用这个effect: () => { document.title = 'You clicked 1 times' }。
    React: 没问题。开始更新UI,喂浏览器,我修改了DOM。
    Browser: 酷,我已经将更改绘制到屏幕上了。
    React: 好的, 我现在开始运行属于这次渲染的effect
    
    运行 () => { document.title = 'You clicked 1 times' }。
    
    
    // After another click, our function is called again
    function Counter() {
      // ...
      useEffect(
        // Effect function from third render
        () => {
          document.title = `You clicked ${2} times`;
        }
      );
      // ..
    }
    

    1 .所有effects都会在渲染之后依次执行,概念上他只是组件输出的一部分,并且可以看到某次特定渲染的props和state
    2 .于此相反的是之前的this.state.count,这种模式,只是会获取到最新的数据

    effect清理上次的函数

    1 .错误的认识:假设第一次渲染的时候props是{id: 10},第二次渲染的时候是{id: 20}
    
    React 清除了 {id: 10}的effect。
    React 渲染{id: 20}的UI。
    React 运行{id: 20}的effect。
    
    1 .正确的认识
    React 渲染{id: 20}的UI。
    浏览器绘制。我们在屏幕上看到{id: 20}的UI。
    React 清除{id: 10}的effect。
    React 运行{id: 20}的effect。
    2 .effect的清除并不会读取最新的props,他只能读取到定义他的那次渲染的props值
    

    1 .react会根据我们当前的props,state同步到DOM
    2 .useEffect使你能够根据props和state同步react tree之外的东西
    3 .react并不能区分effects的不同,所以为了避免不重复的调用,可以给useEffect一个依赖数组参数

    useEffect(() => {
        document.title = 'Hello, ' + name;
      }, [name]); // Our deps
    这好比你告诉React:“Hey,我知道你看不到这个函数里的东西,但我可以保证只使用了渲染中的name,别无其他。”
    
    如果当前渲染中的这些依赖项和上一次运行这个effect的时候值一样,因为没有什么需要同步React会自动跳过这次effect:
    

    4 .即使依赖数组中只有一个值在两次渲染中不同,也不会跳过effect的运行,会同步所有

    移除依赖的常见方式

    1 .让Effect自给自足,但是这种使用场景及其优先。放弃了外面修改state的权力

    useEffect(() => {
        const id = setInterval(() => {
          setCount(count + 1);
        }, 1000);
        return () => clearInterval(id);
      }, [count]);
    
    useEffect(() => {
        const id = setInterval(() => {
          setCount(c => c + 1);
        }, 1000);
        return () => clearInterval(id);
      }, []);
    

    2 .使用reducer,解耦行为和操作数据的两个过程。是当你dispatch的时候,React只是记住了action - 它会在下一次渲染中再次调用reducer。在那个时候,新的props就可以被访问到,而且reducer调用也不是在effect里。

    这就是为什么我倾向认为useReducer是Hooks的“作弊模式”。它可以把更新逻辑和描述发生了什么分开。结果是,这可以帮助我移除不必需的依赖,避免不必要的effect调用。
    3 .把函数移到effects里面

    function SearchResults() {
      const [query, setQuery] = useState('react');
    
      useEffect(() => {
        function getFetchUrl() {
          return 'https://hn.algolia.com/api/v1/search?query=' + query;
        }
    //这个effect依赖的函数1
    
        async function fetchData() {
          const result = await axios(getFetchUrl());
          setData(result.data);
        }
    //这个effect以来的函数2
        fetchData();
      }, [query]); // ✅ Deps are OK
    
      // ...
    }
    

    4 .实在不能放到effects里面的情况

    1 .组件内有几个effect使用了相同的函数,不像在每个里面都复制粘贴一遍
    function SearchResults() {
      function getFetchUrl(query) {
        return 'https://hn.algolia.com/api/v1/search?query=' + query;
      }
    
      useEffect(() => {
        const url = getFetchUrl('react');
        // ... Fetch data and do something ...
      }, []); // 🔴 Missing dep: getFetchUrl
    
    //没使用依赖,那么这个请求之后发生依次,之后请求路径或者参数变化的时候也不会再次请求,其实这里也不能这么说,因为其实参数已经确定了,query在这里是固定的,除非有第三个地方来修改query
      useEffect(() => {
        const url = getFetchUrl('redux');
        // ... Fetch data and do something ...
      }, []); // 🔴 Missing dep: getFetchUrl
    
      // ...
    }
    
    2 .如果把函数加到依赖里面呢
    function SearchResults() {
      // 🔴 Re-triggers all effects on every render
      function getFetchUrl(query) {
        return 'https://hn.algolia.com/api/v1/search?query=' + query;
      }
    
      useEffect(() => {
        const url = getFetchUrl('react');
        // ... Fetch data and do something ...
      }, [getFetchUrl]); // 🚧 Deps are correct but they change too often
    
      useEffect(() => {
        const url = getFetchUrl('redux');
        // ... Fetch data and do something ...
      }, [getFetchUrl]); // 🚧 Deps are correct but they change too often
    
      // ...
    }
    //每次渲染其实函数都是新的,所以这个依赖完全是废的,其实还是每次刷新都触发effect执行,也就是请求数据
    
    
    //解决方法1 :如果一个函数没有使用组件内的任何值,就把他提到组件外面定义,然后就可以自由的在effect里面使用
    function getFetchUrl(query) {
      return 'https://hn.algolia.com/api/v1/search?query=' + query;
    }
    
    function SearchResults() {
      useEffect(() => {
        const url = getFetchUrl('react');
        // ... Fetch data and do something ...
      }, []); // ✅ Deps are OK
    
      useEffect(() => {
        const url = getFetchUrl('redux');
        // ... Fetch data and do something ...
      }, []); // ✅ Deps are OK
    
      // 此时不需要再设为依赖,因为他不在渲染范围内,不会被数据流影响,不会因为props,state而改变
    }
    
    //解决方法2:包装为useCallck hook
    function SearchResults() {
      // ✅ Preserves identity when its own deps are the same
      const getFetchUrl = useCallback((query) => {
        return 'https://hn.algolia.com/api/v1/search?query=' + query;
      }, []);  // ✅ Callback deps are OK
    
      useEffect(() => {
        const url = getFetchUrl('react');
        // ... Fetch data and do something ...
      }, [getFetchUrl]); // ✅ Effect deps are OK
    
      useEffect(() => {
        const url = getFetchUrl('redux');
        // ... Fetch data and do something ...
      }, [getFetchUrl]); // ✅ Effect deps are OK
    
      // 本质上是给函数添加了一层依赖检查,只有函数依赖的参数发生变化,函数才会变化,而不是仅仅简单的去掉函数的依赖,那么effect依赖数组的检查是不是也可也以这么来??
    }
    
    

    相关文章

      网友评论

          本文标题:hook注意事项

          本文链接:https://www.haomeiwen.com/subject/horscktx.html