React hooks实践

前言

最近要对旧的项目进行重构,统一使用全新的react技术栈。同时,咱们也决定尝试使用React hooks来进行开发,可是,因为React hooks崇尚的是使用(也只能使用)function component的形式来进行开发,而不是class component,所以,整个开发方式也会与以前产生比较大的差别。因此,我这里就积累了下实际项目中遇到的问题以及思考,看下能不能帮助你们少走弯路。javascript

正文

接下来就直接进入正文。我会将项目中遇到的问题一一列举出来,而且给出解决方案。html

执行初始化操做的时机

当我转到React hooks的时候,首先就遇到了这个问题:java

通常来讲,业务组件常常会遇到要经过发起ajax请求来获取业务数据而且执行初始化操做的场景。在使用class component编程的时候,咱们就能够在class component提供的生命周期钩子函数(好比componentDidMount, constructor等)执行这个操做。但是若是转到React hooks以后,function component里是没有这个生命周期钩子函数的,那这个初始化操做怎么办呢?总不能每次遇到这种场景都使用class component来作吧?react

解决方案:使用useEffect(想知道useEffect是什么的话,能够点击这里)git

useEffect,顾名思义,就是执行有反作用的操做,你能够把它当成componentDidMount, componentDidUpdate, and componentWillUnmount 的集合。它的函数声明以下github

useEffect(effect: React.EffectCallback, inputs?: ReadonlyArray<any> | undefined)
复制代码

那么,咱们在实际使用中,咱们就可使用这个来执行初始化操做。举个例子ajax

import React, { useEffect } from 'react'

export function BusinessComponent() {
  const initData = async () => {
    // 发起请求并执行初始化操做
  }
  // 执行初始化操做,须要注意的是,若是你只是想在渲染的时候初始化一次数据,那么第二个参数必须传空数组。
  useEffect(() => {
    initData();
  }, []);

  return (<div></div>);
}
复制代码

须要注意的是,这里的useEffect的第二个参数必须传空数组,这样它就等价于只在componentDidMount的时候执行。若是不传第二个参数的话,它就等价于componentDidMount和componentDidUpdate编程

作一些清理操做

因为咱们在实际开发过程当中,常常会遇到须要作一些反作用的场景,好比轮询操做(定时器、轮询请求等)、使用浏览器原生的事件监听机制而不用react的事件机制(这种状况下,组件销毁的时候,须要用户主动去取消事件监听)等。使用class Component编程的时候,咱们通常都在componentWillUnmount或者componentDidUnmount的时候去作清理操做,但是使用react hooks的时候,咱们如何作处理呢?数组

解决方案:使用useEffect第一个参数的返回值浏览器

若是useEffect的第一个参数返回了函数的时候,react会在每一次执行新的effects以前,执行这个函数来作一些清理操做。所以,咱们就可使用它来执行一些清理操做。

例子:好比咱们要作一个二维码组件,咱们须要根据传入的userId不断轮询地向后台发请求查询扫描二维码的状态,这种状况下,咱们就须要在组件unmount的时候清理掉轮询操做。代码以下:

import React, { useEffect } from 'react'

export function QRCode(url, userId) {
  // 根据userId查询扫描状态
  const pollingQueryingStatus = async () => {
  }
  // 取消轮询
  const stopPollingQueryStatus = async() => {
  }

  useEffect(() => {
    pollingQueryingStatus();

    return stopPollingQueryStatus;
  }, []);

  // 根据url生成二维码
  return (<div></div>)
}
复制代码

这样的话,就等价于在componentWillUnmount的时候去执行清理操做。

可是,有时候咱们可能须要执行屡次清理操做。仍是举上面的例子,咱们须要在用户传入新的userId的时候,去执行新的查询的操做,同时咱们还须要清除掉旧的轮询操做。想一下怎么作比较好。

其实对这种状况,官方也已经给出了解决方案了,useEffect的第二个参数是触发effects的关键,若是用户传入了第二个参数,那么只有在第二个参数的值发生变化(以及首次渲染)的时候,才会触发effects。所以,咱们只须要将上面的代码改一下:

import React, { useEffect } from 'react'

export function QRCode(url, userId) {
  // 根据userId查询扫描状态
  const pollingQueryingStatus = async () => {
  }

  const stopPollingQueryStatus = async() => {
  }
  // 咱们只是将useEffect的第二个参数加了个userId
  useEffect(() => {
    pollingQueryingStatus();

    return stopPollingQueryStatus;
  }, [userId]);

  // 根据url生成二维码
  return (<div></div>)
}
复制代码

咱们只是在useEffect的第二个参数数组里,加入了一个userId。这样的话,userId的每一次变化都会先触发stopPollingQueryStatus,以后再执行effects,这样就能够达到咱们的目的。

useState与setState的差别

react hooks使用useState来代替class Component里的state。但是,在具体开发过程当中,我也发现了一些不一样点。useState介绍能够点击这里

在setState的时候,咱们能够只修改state中的局部变量,而不须要将整个修改后的state传进去,举个例子

import React, { PureComponent } from 'react';

export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      name: 'cjg',
      age: 18,
    }
  }

  handleClick = () => {
    const { count } = this.state;
    // 咱们只须要传入修改的局部变量
    this.setState({
      count: count + 1,
    });
  }

  render() {
    return (
      <button onClick={this.handleClick}></button>
    )
  }
}
复制代码

而使用useState后,咱们修改state必须将整个修改后的state传入去,由于它会直接覆盖以前的state,而不是合并以前state对象。

import React, { useState } from 'react';

export function Count() {
  const [data, setData] = useState({
    count: 0,
    name: 'cjg',
    age: 18,
  });
    
  const handleClick = () => {
    const { count } = data;
    // 这里必须将完整的state对象传进去
    setData({
      ...data,
      count: count + 1,
    })
  };

  return (<button onClick={handleClick}></button>)
}
复制代码

减小没必要要的渲染

在使用class Component进行开发的时候,咱们可使用shouldComponentUpdate来减小没必要要的渲染,那么在使用react hooks后,咱们如何实现这样的功能呢?

解决方案:React.memouseMemo

对于这种状况,react固然也给出了官方的解决方案,就是使用React.memo和useMemo。

React.memo

React.momo其实并非一个hook,它其实等价于PureComponent,可是它只会对比props。使用方式以下(用上面的例子):

import React, { useState } from 'react';

export const Count = React.memo((props) => {
  const [data, setData] = useState({
    count: 0,
    name: 'cjg',
    age: 18,
  });
  
  const handleClick = () => {
    const { count } = data;
    setData({
      ...data,
      count: count + 1,
    })
  };

  return (<button onClick={handleClick}>count:{data.count}</button>)
});

复制代码

useMemo

useMemo它的用法其实跟useEffects有点像,咱们直接看官方给的例子

function Parent({ a, b }) {
  // Only re-rendered if `a` changes:
  const child1 = useMemo(() => <Child1 a={a} />, [a]);
  // Only re-rendered if `b` changes:
  const child2 = useMemo(() => <Child2 b={b} />, [b]);
  return (
    <> {child1} {child2} </> ) } 复制代码

从例子能够看出来,它的第二个参数和useEffect的第二个参数是同样的,只有在第二个参数数组的值发生变化时,才会触发子组件的更新。

总结

一开始在从class component转变到react hooks的时候,确实很不适应。但是当我习惯了这种写法后,个人心情以下:

固然,如今react hooks仍是在alpha阶段,若是你们以为不放心的话,能够再等等。反正我就先下手玩玩了哈哈哈。

本文地址在->本人博客地址, 欢迎给个 start 或 follow

相关文章
相关标签/搜索