ReactJS Hooks 总结

作者: ZHG | 来源:发表于2021-09-27 20:40 被阅读0次

    Hooks 定义

    Hook 就是钩子的意思。为了在函数组件中使用类组件的生命周期、状态管理等一些类组件特性,所以引入了 hooks.
    需要注意的是:在类组件中是不能够使用hooks的。

    Hooks 使用规则

    • Hooks只能在顶层调用,不能在循环、条件判断或者嵌套函数中调用hook函数。
    // 错误写法 
    if(a===b){
      useEffect(()=>{
        //...
      },[...])
    }
    
    • 只能在函数组件中使用hooks,不能在类组件中使用
    • 可以使用 eslint-plugin-react-hooks插件规范hooks代码

    Hooks 优缺点

    优点

    • 代码容易复用(可以自定义useState)
    • 代码可读性强。代码好看些,每个功能都在函数中代码量少
    • 组件层级变少
    • 不需要考虑this 的指向问题

    缺点

    • 响应式的 useEflect , useCalbask 的作用、第二个参数(依赖项数组)的改变时机不容易把握。
    • 状态不同步,不在同一个 ui 线程,同一个变量,会显示不同的值。可以使用 useRef 改善。
    • 状态管理容易出错
    • 需要了解所有 hooks 函数

    函数列表

    useState

    • 用来声明状态变量,类似类组件class 中的this.setState({a:'abc'})。而且函数的渲染每次都是独立的(异步),这也就是 Capture Value 特性
    • 示例
    // 定义
    const [nameState, setName] = useState("");// useState 的参数为设定的默认值
    const [age, setAge] = useState(1);
    // 使用
    setName("ABC");
    setAge(25);
    
    • 点击按钮改变nameState值,当回车 Enter(或者不在同一个线程中,初始时注册的方法等)提交时,获取 nameState 值,这时发现nameState还是默认的初始值。例如:点击按钮提交和按回车键提交,点击按钮提交是可以获取到值的,按回车键提交获取值失败。
    • 解决方法:
      • 方法一
    const [nameState,setName]=useState();
    const lastNameState = useRef(nameState);
    // 改变 lastNameState 值 
    useEffect(()=>{
      lastNameState.current= nameState;
    }[nameState]);
    // 点击按钮时,调用setName 
    const onClick=(e)=>{
      setName(e.currentTarget.value)
    }
    
    • 方法二
    useEffect(()=>{
      setName(name)
    },[name]);
    //点击按钮时,发送一个action,改变reducer 的state值 
    const onClick=(e)=>{
      putName(e.currentTarget.value)
    }
    //...
    // Action
    export function putName(name){
      return{
        type:"CHANGE_NAME", 
        payload: { name }
      }
    }
    

    useEffect

    • 网上都叫其副作用,我也不明白为啥都叫副作用 ♂️,我更倾向于叫响应。
    • 其替代类组件声明周期函数: componentDidMount、componentWillUnmount、componentDidUpdate。
    • 示例
    // componentDidMount || componentWillUnmount
    useEffect(()=>{
    // componentDidMount
    // ...
    
    // componentWillUnmount 
    return ()=>{
      //...
    },[]);
    
    // componentDidMount || componentDidUpdate 
    useEffect(()=>
      setName(name)
    ),[name]);// 从父组件传递过来的name,当name 发生变化时,更新nameState
    

    useContext

    • 用来处理多层级传递数据,减少组件的嵌套。
    • 示例
    function Children(){
      const color = useContext(colorContext);
      return <div>{color}</div>
    }
    
    function Parent(){
      return <Children/>
    }
    
    const colorContext = createContext("gray");// React.createContext();
    function App(){
      return (
        <colorContext.Provider value={"red"}>
          <Parent/>
        </colorContext.Provider>
      )
    }
    

    useReducer

    • 可以看成是react-redux 使用方式的缩小版。不能使用redux 提供的中间件。
    • 可以看成是useState 的替代方案
    • useReducer(reducer, initialState, init())。reducer函数,接收state和action; initialState是需要设定的state的初始值;init()是通过方法来初始化state,这样可以情性的创建初始state。
    • 示例
    const initialState =0;
    const init = (v) =>{
        return v;
    }
    
    const testReducer =(state, action)=>{
        switch(action){
            case "add":
                return state+1; 
            case"sub":
                return state-1; 
            case "reset":
                return init(action.payload); 
            default:
                return state;
        
        }
    }
    
    const[count, calculationDispatch]= useReducer(testReducer, initialState);
    // const[count, calculationDispatch] = useReducer(testReducer, initialState, init); 
    return(
        <div>
            <div>{count}</div>
            <button onClick={()=>calculationDispatch("add")}>ADD</button>
            <button oClick={()=>calculationDispatch("sub")}>SUB</button>
            <button oClick={()=>calculationDispatch({type: "reset",payload:initialState})}>RESET</button>
        </div>
    )
    

    useCallback

    • 获得一个记忆函数,避免在某些情况下子组件的重复渲染。一般用来做性能优化。
    • useCallback 返回的是一个函数
    • useCallback(function,[...])。两个参数,第一个参数是需要记忆的画数,第二个参数是当数组内的值发生变化了,才执行function。
    • 示例
    const [text, setText] = useState(""); 
    const submit = useCallback(()=>{
        console.log(text)
    }, [text]);
    
    return(
        <div>
            <input value={text} onChange={(e)=>setText(e.target.value)}/>
            <button onClick={submit}>Submit</button>
        </div>
    )
    

    useMemo

    • 获得一个记忆组件,适用于返回确定的值
    • 类似useCallback,但是useCallback返回的是需要执行的函数,而useMemo 返回的是执行函数返回的值
    • 示例
    const [text, setText] = useState(""); 
    const textMemo = useMemo(()=>{
        return text+"time"
    }, [text]);
    return(
        <div>
            <input value= {text} onChange={(e)=>setText(e.target.value)}/>
            <input value={textMemo}/>
        </div>
    )
    

    useRef

    • 生成对DOM对象的引用。
    • 类似于 createRef,但是它是真正的引用,而不是把值copy 过去。
    • 与useState很像,可以把它看成是render的全局变量,而useState是每个render中局部变量; useRef.current 不会触发Ul的重新渲染,而useState会触发Ul的重新渲染。
    • 示例
    const [name, setName] = useState("");
    const nameRef = useRef(name);
    const submit =() =>{
        setName(nameRef.current);
    }
    return(
        <div>
            <p>{name}</p>
            <div>
                <input ref ={nameRef}/> 
                <button onClick={submit}>Submit</button>
            </div>
        </div>
    )
    

    useImperativeHandle

    • 穿透Ref,用于父组件获取子组件的引用
    • 示例
    function ChildInputComponent(props, ref){
        const inputRef = useRef(null);
        useImperativeHandle(re, ()=>inputRef.current); 
        return(
            <div>
                <input name="input1" ref={inputRef} placeholder="yes"/>
                <input name="input1" placeholder="nonono"/>
            </div>
        )
    }
    
    const ChildInput = forwardRef(ChildInputComponent);
    function App(){
        const inputRef = useRef(null);
        useEffect(()=>{
            inputRef.current.focus()
        },[]); 
        
        return(
            <div>
                <ChildInput ref={inputRef}/>
            </div>
        )
    }
    

    useLayoutEffect

    • 同步执行,在页面完全渲染完成后,操作 dom, 会优于 useEffect 异步触发函数。

    其他

    memo

    • 用来包裹函数式组件的
    • 当用React.memo包裹组件时,当组件内的useState或useContext 发生变化时,才会重新渲染面面。
    • memo(Component, propsAreEqual(preProps, nextProps));第一个参数为函数组件,第二个参数为比较规则,若不传递则默认为props 浅比较。

    forwardRef

    • 主要用来进行DOM 穿透的
    • 示例
    const AButton = React.forwardRef((props, ref)=>(
        <button ref={ref} {...props}>
        {props.children}</button>
    ))
    
    class BButton extends React.Component{
        this.testRef = React.createRef();
        onClick =()=>{
            // 打印出 AButton 中button 的信息 
            console.log(this.testRef.current)
        };
    
        render(){
            return(
                <div>
                    <AButton ref = {this.testRef} onClick={onClick}>AButtan</AButton>
                </div>
            )
        }
    
    }
    

    总结

    这里只罗列了hooks 函数的基本用法,想要更深层次的掌握react 的知识,需要不断的去实践。只有在不断的使用各种技巧、各种功能,才会越发的了解掌握更深入的内容。

    相关文章

      网友评论

        本文标题:ReactJS Hooks 总结

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