react—hooks

吐槽君 分类:javascript

1.react的hooks是干什么的

说白了,就是拿function当组件,因为之间用react定义组件用的class关键字,人家嫌麻烦,代码量太大,就折腾了这货,直接一个function就是一个组件。

那么,原来用class定义的组件里可以写state,有生命周期,这货一个function怎么实现那些功能呢?

下面就来介绍一下怎么在function里处理state和生命周期的。

2.useState

hook的useState就是class里面的state,从它里面可以声明组件里用到的state,举个例子

class写法

class Example extends react.Component {
    constructor(props){
        this.state = {
            count: 0,
        }
    }
    render() {
        return (
            <div>{this.state.count}</div>
        )
    }
}
 

hook写法

function Example(){
    const {count, setCount} = useState(0);
    return <div>{count}</div>
}
 

简单吧,声明一个state,就要附带一个setXXX,这就相当于setState(),比如这里声明的是count,那么想更新值就要声明一个setCount变量(他也是一个方法),通过setCount(1)更新count的值后,组件就会重新渲染

useState()方法中的参数是默认值的意思,上面count我想让它是一个init类型的值,就给了默认值为0,这次声明了个data,我想让他是一个数组,就给了个[]

更新state,如下例子

function Example() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>clicked {count}</button>
    </div>
  );
}
 

当然如果不想直接在onClick事件里调用 setCount 方法,想自定义方法来更新count的值,也可以在这个function组件里再定义一个方法来处理

function Example() {
  const [count, setCount] = useState(0);

  const updateCount = function() {
    setCount(count + 1);
  };

  return (
    <div>
      <button onClick={() => updateCount()}>clicked {count}</button>
    </div>
  );
}
 

怎么在useState里声明多个变量呢?

function Example() {
  const [state, setState] = useState({
    count: 0,
    data: [1, 2, 3]
  });

  const updateCount = function() {
    setState({ ...state, count: state.count + 1 });
  };

  return (
    <div>
      <ul>
        {state.data.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
      <button onClick={() => updateCount()}>clicked {state.count}</button>
    </div>
  );
}
 

3.useReducer

除了从 useState 里声明state外,还可以用另一个hook useReducer ,用法类似于 redux 里的 reducer

function Example() {
  const [count, dispatch] = useReducer((count, { type, action }) => {
    switch (type) {
      case "update":
        return action.payload;
      default:
        break;
    }
  }, 0);

  return (
    <div>
      <button
        onClick={() =>
          dispatch({ type: "update", action: { payload: count + 1 } })
        }
      >
        clicked {count}
      </button>
    </div>
  );
}
 

相同的 useReducer 最后一个参数也是默认值, 通过dispatch方法传入类型然后修改state;

那么如何在 useReducer 里声明多个变量呢?

function Example() {
  const [state, dispatch] = useReducer(
    (state, { type, action }) => {
      switch (type) {
        case "update":
          return { ...state, count: state.count + 1 };
        case "add":
          return { ...state, data: [...state.data, action.payload + 1] };
        default:
          break;
      }
    },
    { count: 0, data: [1, 2, 3] }
  );

  return (
    <div>
      <ul>
        {state.data.map(item => (
          <li key={item}>{item}</li>
        ))}
      </ul>
      <button onClick={() => dispatch({ type: "update" })}>
        clicked {state.count}
      </button>
      <button
        onClick={() =>
          dispatch({ type: "add", action: { payload: state.data.length } })
        }
      >
        add
      </button>
    </div>
  );
}
 

4.useEffect

开发过react组件的人都知道,react组件里有很多生命周期,这些生命周期在 function 组件里是怎么实现的呢?这时就要用到 useEffect 了。

首先是 componentDidMount 和 componentDidUpdate 两个生命周期,它俩在function组件里的实现如下:

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("hello world");
  });

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>clicked {count}</button>
    </div>
  );
}
 

上面代码运行后,可以看到浏览器的console里会打印出 hello world 这个打印就相当于是 componentDidMount 被执行了。

点击button,count每次都会变化,也就是说state一直在变,然后会发现浏览器的 console 里会随着button点击后count变化也会打印 hello world 这时候就相当于 componentDidUpdate 在执行,每次state有变动的时候, useEffect 都会执行一次。

那有没有办法不让它执行呢?有!

useEffect 有两个参数,第一个参数是个函数,第二个参数相当于条件,看一下面的例子:

function Example() {
  const [state, setState] = useState({ count: 0, data: [1, 2, 3] });

  useEffect(() => {
    console.log("hello world");
  }, [state.count]);

  return (
    <div>
      <ul>
        {state.data.map(item => (
          <li key={item}>{item}</li>
        ))}
      </ul>
      <button onClick={() => setState({ ...state, count: state.count + 1 })}>
        clicked {state.count}
      </button>
       
      <button onClick={() => setState({ ...state, data: [...state.data, state.data.length + 1] })}>add</button>
    </div>
  );
}
 

当页面渲染完后,浏览器console里会有一个 hello world 输出,可以看见我在 useEffect的第二个参数上加了个参数 state.count 这时候它就会以 state.count 为校验目标,只有当 state中的count有变化时,useEffect 里的第一个参数(函数)才会执行。

也就是说当点击第二个按钮的时候,state更新的只是data变量,count变量没有变化,useEffect里的第一个函数参数就不会执行,这第二个参数也就相当于是 shouldComponentUpdate 这个生命周期了。

最后再说一下组件卸载时的生命周期 componentWillUnmount , 要在function组件里实现这个功能也很简单,只需要在 useEffect 里返回一个函数就可以了。

function Example() {
  const [show, setShow] = useState(false);
  return (
    <div className="App">
      {show ? null : <Item />}
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button onClick={() => setShow(!show)}>show/hidden</button>
    </div>
  );
}

function Item() {
  useEffect(() => {
    console.log("子组件加载");
    // 返回的函数名无所谓,可以随便起
    return function bye() {
      console.log("子组件卸载");    //子组件在卸载时可以在这个函数里进行数据初始化等操作
    };
    // return () => {  // 可以写成匿名函数
    //   console.log("子组件卸载");
    // };
  });
  return <div>我是子组件</div>;
}
 

当点击按钮时,子组件会交替的显示和不显示,也就意味着子组件在交替的加载和卸载,在浏览器的console里就可以看到 子组件加载 和 子组件卸载 的日志。

5.父子组件传值

function组件里父子组件传值很简单,因为它本身就是 function,所以传的值就是方法的参数,如下:

function App() {
  const [data, setData] = useState([1, 2, 3]);
  return (
    <div className="App">
      <Item items={data} />
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button onClick={() => setData([...data, data.length + 1])}>add</button>
    </div>
  );
}

function Item({ items }) {
  useEffect(() => {
    console.log("子组件加载");
    // 返回的函数名无所谓,可以随便起
    return function bye() {
      console.log("子组件卸载");
    };
    // return () => {  // 可以写成匿名函数
    //   console.log("子组件卸载");
    // };
  });
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
}
 

回复

我来回复
  • 暂无回复内容