hooks

作者: lovinglili | 来源:发表于2019-08-01 18:14 被阅读0次

    简述

    Hooks?

    hooks 是 React v16.7.0-alpha 加入的新特性,可以在 class 外使用 state 以及其他 React 新特性。

    优点

    • 相比类来说,相似的逻辑不再分隔开,代码更简洁

    useState

    1. 和类的对比

    一个计数小例子(来自官网~)

    类方法实现:
    import React from 'react';
    class Example extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          count: 0
        };
      }
    
      render() {
        return (
          <>
            <div>You clicked {this.state.count} times</div>
            <button onClick={() => this.setState({ count: this.state.count + 1 })}>add</button>
            <button onClick={() => this.setState({ count: this.state.count - 1 })}>reduce</button>
          </>
        );
      }
    }
    export default Example
    
    useState 实现:
    import React,{ useState } from 'react';
    import  styles from './main.module.less';
    
    function Example() {
      const [count,setCount] = useState(1);
      return (
        <>
        <div className={styles.container}>you clicked {count} times</div>
        <button onClick={()=>setCount(count+1)}>add</button>
        <button onClick={()=>setCount(count-1)}>reduce</button>
      </>
      );
    }
    
    export default Example;
    
    1. useState
    const [count,setCount] = useState(1);
    // useState 返回的是一对值,如下:
    var countStateVariable = useState(1); 
    var count = countStateVariable[0]; // 第一个元素
    var setCount = countStateVariable[1]; // 第二个元素
    
    // 使用的是js的"数组破坏",官方解释使用[0]和[1]会有很大的迷惑性~
    

    多个 state 怎么写?我们可以使用多个 useState.

    function Example() {
      const [count,setCount] = useState(1);
      const [age,setAge] = useState(10);
      const [fruit,setFriute] = useState('apple');
      const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
      
      return (
        <>
        <div className={styles.container}>you clicked {count} times</div>
        <button onClick={()=>setCount(count+1)}>add</button>
        <button onClick={()=>setCount(count-1)}>reduce</button>
      </>
      );
    }
    

    那么React是怎么记住"哪个状态对应哪个 useState"? 答案就是依赖调用钩子函数的顺序。所以存在了几条规范

    useEffect

    useEffect 允许你在功能组件里面执行副作用

    • 和类的对比
    类方法实现:
    class Example extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          count: 0,
          data:{},
        };
      }
    
      componentDidMount(){
        fetch('/api/xiaoyi/goodsList').then(res=>res.json()).then(data=>{
          console.log(data,'data')
          setState({data})
        })
        window.addEventListener('resize', this.getHeight);
      }
      
      componentDidUpdate(preProps){
        if(preProp.id!==this.props.id){
          fetch('/api/xiaoyi/goodsList').then(res=>res.json()).then(data=>{
            console.log(data,'data')
          })
        }
      }
    
       componentWillUnmount() {
        window.remoteEventListener('resize', this.getHeight);
      }
      
      render() {
        return (
          <>
            <div>You clicked {this.state.count} times</div>
            <button onClick={() => this.setState({ count: this.state.count + 1 })}>add</button>
            <button onClick={() => this.setState({ count: this.state.count - 1 })}>reduce</button>
          </>
        );
      }
    }
    export default Example
    
    useEffect 实现:
    import React,{ useState, useEffect } from 'react';
    import  styles from './main.module.less';
    
    function Example(props) {
      const [count,setCount] = useState(1);
      useEffect(()=>{
        fetch('/api/xiaoyi/goodsList').then(res=>res.json()).then(data=>{
          console.log(data,'data')
        })
        window.addEventListener('resize', ()=>{console.log(111)});
        
        return () => {
          window.removeEventListener('resize', ()=>{console.log(111)});
            }
      },[props.id])
      return (
        <>
        <div className={styles.container}>you clicked {count} times</div>
        <button onClick={()=>setCount(count+1)}>add</button>
        <button onClick={()=>setCount(count-1)}>reduce</button>
    
      </>
      );
    }
    
    export default Example;
    

    Hooks允许我们根据代码所做的而不是生命周期方法名来分割代码。

    useEffect 的参数是一个函数,组件每次渲染之后,都会调用这个函数参数,这样就达到了 componentDidMount 和 componentDidUpdate 一样的效果。

    useEffect 第一个参数返回的一个函数相当于清除该副作用,会在每次渲染执行。

    为什么不在组件销毁的时候执行?<br /> 因为如果要销毁的内容中间发生改变,在销毁的时候,销毁的就是之前的内容,不是当前要销毁的了,就会产生bug。

    useEffect 的第二个参数,代表重新渲染如果这个值未发生变化,就跳过渲染。

    如果要运行一个 effect 并只清理一次(在装载和卸载时),可以将空数组([])作为第二个参数传递。这告诉react,您的效果不依赖于来自props或state的任何值,因此它不需要重新运行。这并不是一个特殊的情况——它直接来自于输入数组的工作方式。

    rules

    使用 hooks 的规则
    • 不要在循环,条件或嵌套函数内使用钩子函数。相反,要在函数顶层使用钩子。
    • 不要在常规的js函数里面使用钩子函数
    • 可以自定义的钩子函数里面使用
      <a name="WluJO"></a>
    react 提供了esLint插件 [eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks) 来帮助我们执行这些规则
    • npm install eslint-plugin-react-hooks@next
    规则由来

    由上面可知,单个组件使用多个 State 或者 Effect 钩子函数时候,React 依赖调用钩子函数的顺序记住相对应的状态的。所以当使用条件判断时候:

    // 打破了第一条规则
      if (name !== '') {
        useEffect(function persistForm() {
          localStorage.setItem('formData', name);
        });
      }
    

    当第一次render,name!==''为true的时候,这个钩子函数会执行;当下一次为false的时候,渲染就会跳过这个钩子函数。此时钩子的调用顺序发生了改变。(加入这个钩子函数之前和之后都存在其他钩子函数)

    useState('loving')          
    // useEffect(persistForm)  // 这个 hook 将会被跳过
    useState('lili')        // 现在读取是2,但是之前是3,读取这个状态变量就会出错。
    

    react不知道第二次useState hook调用返回什么。react预期此组件中的第二个hook调用与PersisteForm效果相对应,就像在上一次呈现期间一样,但它不再如此。从那时起,我们跳过的钩子调用之后的每一个钩子调用也会一个接一个地移动,从而导致错误。

    useEffect(function persistForm() {
        // 👍不会打破第一条规则
        if (name !== '') {
          localStorage.setItem('formData', name);
        }
      });
    

    参考文档:https://www.reactjscn.com/docs/hooks-state.html

    相关文章

      网友评论

          本文标题:hooks

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