react hooks的简单使用

hooks解决的问题

  • 带组件状态的逻辑难以重用;
  • 复杂组件难以理解;
  • class组件难以理解;

什么是hooks

简单理解,hooks是函数,每个hook都是function component提供使用react状态和生命周期特性的通道。hooks不能在class component中使用。
react提供了一些预定好的hooks,本文主要介绍useState和useEffect。

1、state hook

传统使用

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

state hook改写后

import React, { useState } from 'react';

function Example() {
  // 定义一个 state 变量 count,变量值可以通过 setCount 来改变,变量初始值为 0
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

简单介绍:
useState的入参只有一个,就是state的初始值,可以是数字、字符串或对象,甚至函数(这一函数只会在组件初始化渲染时执行)。

// 入参为函数形式时的处理
const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

useState的返回值是一个数组,数组的第一个元素是state的当前值,第二个元素是改变state的方法。每次更新都会被记录状态,并可以在全局使用。实现原理如下

var _state; // 把 state 存储在外面

function useState(initialValue) {
  _state = _state || initialValue; // 如果没有 _state,说明是第一次执行,把 initialValue 复制给它
  function setState(newState) {
    _state = newState;
    render();  // 模拟 reRender,不需要关心
  }
  return [_state, setState];
}

2、effect hook

传统使用

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }

  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

effect hook改写后

import React, { useState, useEffect } from 'react';

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

  useEffect(() => {
    // 同时具备渲染、更新、卸载三个生命周期的执行时机
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

简单介绍:
传统写法中,副作用代码写在了componentDidMount和componentDidUpdate中,而且两个生命周期中的代码一样,effect hook改写后就不会有这个问题。
useEffect会在每次DOM渲染后执行,不会阻塞页面渲染,而且同时具备componentDidMount、componentDidUpdate和componentWillUnmount三个生命周期函数的执行时机

最简单的用法

useEffect(() => {
  console.log(count);
}, [count])

由上述代码可以知道useEffect的几个特点:

  • 有两个参数callback和dependencies数组;
  • 如果dependencies数组不存在,那么callback每次render都会执行;
  • 如果dependencies数组存在,只有当它发生变化,callback才会执行;
    实现原理如下
let _deps; // _deps 记录 useEffect 上一次的 依赖

function useEffect(callback, depArray) {
  const hasNoDeps = !depArray; // 如果数组不存在
  const hasChangedDeps = _deps
    ? !depArray.every((el, i) => el === _deps[i]) // 两次的数组是否完全相等
    : true;
  // 数组不存在,或数组发生改变
  if (hasNoDeps || hasChangedDeps) {
    callback();
    _deps = depArray;
  }
}

由上述代码可以解答一个问题:
Q:为什么第二个参数是空数组,相当于componentDidMount?
A:因为依赖一直不变(数组存在且一直为空,即两次的数组总是相等),所以callback不会执行第二次。

还有一些额外的清理工作,如订阅某个功能:
传统使用

class FriendStatus extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOnline: null };
    this.handleStatusChange = this.handleStatusChange.bind(this);
  }

  componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  handleStatusChange(status) {
    this.setState({
      isOnline: status.isOnline
    });
  }

  render() {
    if (this.state.isOnline === null) {
      return 'Loading...';
    }
    return this.state.isOnline ? 'Online' : 'Offline';
  }
}

在componentDidMount订阅后,需要在componentWillUnmount取消订阅,使用effect hook改写后

import React, { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    
    // 返回一个函数,当再次执行副作用代码时 进行额外的清理工作
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

useEffect的返回值是一个函数时,react会在下一次执行这个副作用之前执行一遍清理工作,简单理解为:

组件挂载 --> 执行副作用 --> 组件更新 --> 执行清理函数 --> 执行副作用 --> 组件更新 --> 执行清理函数 --> 组件卸载

FROM:
https://segmentfault.com/a/1190000018928587
https://github.com/brickspert/blog/issues/26
http://www.ruanyifeng.com/blog/2019/09/react-hooks.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值