对react hook的一般理解

921 阅读4分钟
当村口的狗叫了,其它的狗也跟着叫了,但它们不知道为什么叫.

react hook是什么

react hook是什么?引入官网的话,react hook是react16.8新增的新特性,它可以让你在不编写class的情况下,使用state以及其他react特性。

为什么要使用react hook

在react组件有class组件和function组件之分,也可以分状态组件和无状态组件,function组件的性能要比class组件的性能要高。
这里主要说为什么要使用hook
  • this指向问题
  • 状态逻辑复用
  • 关注点分离

react hook的使用

StateHook

StateHook提供了在函数组件中使用state的能力

import React,{useState} from 'react';
function Count() {
    const [count,setCount] = useState(0);
    const [num,setNum] = useState(10);
    return (
        <div>
                <div>
                    <button onClick={()=>setCount(count+1)}>{count}</button>
                </div>
                <div>
                    <button onClick={()=>setCount(num+2)}>{num}</button>
                </div>
            </div>
    )
}
这是一个简单的例子,使用useState返回一个数组,包含两项,第一项是值可以理解为使用class组件时定义在state里面的属性,第二项是改变值的函数,可以理解为setState的一个二层封装。
注意点:
  • 严格按照useState调用的顺序,不能动态改变useState的调用数量和顺序
  • useState接收一个参数作为默认值或者接收函数,都以第一次渲染中调用useState为准,如果是函数,则只会调用一次,返回的值作为默认值

你可能会产生几个问题?

  • 1.stateHook怎么知道是返回这个组件的count,而不是返回其他组件count
  • 2.stateHook怎么知道返回的是count的值还是num的值
useState只会返回该组件的count,个人猜测是按照执行上下文来做判断.
stateHook怎么知道返回的是count的值还是num的值,这里是按照调用顺序来区分,第一次调用返回count的值 第二次返回num值。

EffectHook

函数组件是不支持生命周期函数的,因此EffectHook就提供了这样一个功能。 先说明一下 useEffect接收一个函数和一个数组作为参数,只有在数组中的每一项不改变的情况下才不会重新调用该函数。
因此有以下几种情况:
  • 1.只传递第一个参数,那么这个函数在每次页面渲染之后都会调用,类似componentDidMount和componentDidUpdate
            function App(props) {
                const [count,setCount] = useState(0);
                useEffect(()=>{
        		//每次渲染后都会调用
                	console.log('mount and update');
                });
                ...
            }
            
  • 2.接收两个参数,第一个参数传递的是函数,另一个参数传递的是空数组,那么这个函数只会在页面组件渲染到页面上调用,只会调用一次 类似componentDidMount
            function App(props) {
                const [count,setCount] = useState(0);
                useEffect(()=>{
                //类似componentDidMount 只会调用一次
                	console.log('mount and update');
                },[]);
                ...
            }
            
  • 3.在2的情况下,回调返回一个函数,那么这个函数可以看成是componentWillUnmount,不过有点不一样的是,该函数的调用情况有两种,第一种是组件卸载。第二种是下一次组件开始渲染之前调用
            function App(props) {
                const [count,setCount] = useState(0);
                useEffect(()=>{
                	return ()=>{
                      console.log('mount and update');
                    };
                });
                ...
            }
            
  • 4.第二个参数不是空数组,则只有数组里面的每一项不改变对的时候,才不会触发
            function App(props) {
                const [count,setCount] = useState(0);
                useEffect(()=>{
                	console.log('mount and update');
                },[count===4]);
                ...
            }
            

ContextHook

ContextHook提供函数组件使用context的能力
            const CountText = createContext(1);
            function App(props) {
              const [count,setCount] = useState(0);
              return (
              	...
                <Counttext.Provider value={{count}}>
                       <Tab/>
                 </Counttext.Provider>
                ...
              )
            }
            function Tab(props) { 
            	const context = useContext(CountText);
                return (
                	<div>
                   		 {context.count}
                       </div>
                );
            }
        

MemoHook&&CallbackHook

MemoHook和CallbackHook都是性能优化的手段,类似class组件的shouldComponentUpdate。
      function App() {
          const [count,setCount] = useState(0);
          const double = useMemo(()=>{
              return count * 2;
          },[count===4]);
            return (
               &lt;div&gt;
                    {double}
                  &lt;/div&gt;
            );
      }
    </pre>
  </code>
第二个参数和useEffect的第二个参数的用法一样,和组件一块使用记得使用memo,当useMemo返回函数,则可以使用CallbackHook,可以把CallHook使用MemoHook的一种变种

RefHook

RefHook提供了函数组件操作DOM节点的能力.一般有两种场景会用到RefHook,分别是操作DOM节点或着组件、跨周期共享数据。
  • 1.操作DOM节点
                function App(props) {
                    const [count,setCount] = useState(0);
                    const ref = useRef();
                    const callback = useCallback(function() {
                    	setCount(count=>count+1);
                    })
                    useEffect(()=>{
                    	ref.current.addEventListener('click',callback,false);
                        return ()=>{
                        ref.current.removeEventListener('click',callback,false);
                        };
                    },[]);
                    return ( <div>
                    	       <button ref={ref}>
                                </button>
                              </div>)
                }
                
  • 2.跨周期共享数据(例子不太好)
                function App(props) {
                    let it = useRef(); 
    				const [count,setCount] = useState(()=>0);
    
                useEffect(()=>{
                	 it.current = 4;
                       window.setInterval(()=>{
                        setCount(count=>{
                           return count+1;
                          })
                     },1000);
                },[it]);
     	         console.log(it.current);//undefined 4 4 4 4
               ...
            }
            </pre>
          </code>
          <div>
    

    所以在这个情况下,如果共享数据使用state,那么数据只要改变就会重新渲染,所以采取非state,使用RefHook,怎么理解这里RefHook能够跨周期共享数据,其实可以理解成hooks返回的值与调用顺序有关

</ul>

自定义Hook

自定义Hooks可以是状态逻辑复用,注意点就是Hooksname需要use开头,清晰明了,自定义Hooks其实和普通函数也差不多,可以返回JSX也可以返回其他类型的值
function useCount() {
    const [count,setCount] = useState(()=>0);
    
    const ref = useRef(); 
    useEffect(()=>{
        ref.current = window.setInterval(()=>{
            setCount(count=>{
                if(count+1===10) {
                    window.clearInterval(ref.current);
                }
                return count+1;
            })
           
        },1000);
    },[]);
    return (
        <button ref={ref}>
            {count}
        </button>
   );
}
function App(props) {
 
    const ele = useCount();
    return ele;
}

Hook使用规则

  • 1.只在顶层使用Hook
  • 2.只在函数组件或者自定义Hook里面使用Hook