前言

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

正文

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

执行初始化操作的时机

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

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

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

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

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

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

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

React hooks实践的更多相关文章

  1. 理解 React Hooks

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由志航发表于云+社区专栏 TL;DR 一句话总结 React Hooks 就是在 react 函数组件中,也可以使用类组件(classe ...

  2. React Hooks用法大全

    前言 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖 ...

  3. React Hooks实现异步请求实例—useReducer、useContext和useEffect代替Redux方案

    本文是学习了2018年新鲜出炉的React Hooks提案之后,针对异步请求数据写的一个案例.注意,本文假设了:1.你已经初步了解hooks的含义了,如果不了解还请移步官方文档.(其实有过翻译的想法, ...

  4. React Hooks 实现react-redux

    Redux 是目前 React 系统中最常用的数据管理工具,它落实并发扬了 Flux 的数据单向流动模式,被实践证明为一种成熟可用的模式. 尽管承受着一些非议,Redux 在 React 数据管理界的 ...

  5. 蒲公英 &#183; JELLY技术周刊 Vol.21 -- 技术周刊 &#183; React Hooks vs Vue 3 + Composition API

    蒲公英 · JELLY技术周刊 Vol.21 选 React 还是 Vue,每个人心中都会有自己的答案,有很多理由去 pick 心水的框架,但是当我们扪心自问,我们真的可以公正的来评价这两者之间的差异 ...

  6. 通过 React Hooks 声明式地使用 setInterval

    本文由云+社区发表 作者:Dan Abramov 接触 React Hooks 一定时间的你,也许会碰到一个神奇的问题: setInterval 用起来没你想的简单. Ryan Florence 在他 ...

  7. 初探React Hooks & SSR改造

    Hooks React v16.8 发布了 Hooks,其主要是解决跨组件.组件复用的状态管理问题. 在 class 中组件的状态封装在对象中,然后通过单向数据流来组织组件间的状态交互.这种模式下,跨 ...

  8. [转] React 最佳实践——那些 React 没告诉你但很重要的事

    前言:对很多 react 新手来说,网上能找到的资源大都是些简单的 tutorial ,它们能教会你如何使用 react ,但并不会告诉你怎么在实际项目中优雅的组织和编写 react 代码.用谷歌搜中 ...

  9. React Hooks新特性学习随笔

    React Hooks 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 前言 本篇主要以讲几个常用的api为主. 1.u ...

随机推荐

  1. 16.The Effect of Advertisement 广告的影响

    16.The Effect of Advertisement 广告的影响 (1) The appeal of advertising to buying motives can have both n ...

  2. flag读取控制台参数

    package main import ( "fmt" "os") func main() { arg := os.Args if len(arg) < ...

  3. IO流的种类

    字节流:InputStream(new FileInputStream(new File()); Outpustream(new FileOutputStream(new File()); 字符流:  ...

  4. npm: 权限阻止修复

    在使用 npm install 时,总提示: Error: EACCES: permission denied 今天通过这个命令设置了权限解决了问题: sudo chown -R $(whoami) ...

  5. C++中_cplusplus及Extern "C"的理解

    一.意义 Extern "C":告诉编译器,这部分代码按C语言的方式进行编译,而不是C++的.   _cplusplus:.cpp文件默认定义的宏.用来判断是否为.cpp文件. 二 ...

  6. Linux下使用openVPN连接到某个内网

    推荐一个网站(比较全的介绍关于openvpn的客户端与服务端的配置) 点击我 此处我介绍我配置openvpn客户端连接的坑 我的机器为kali linux apt-get install openvp ...

  7. Android开发者的Anko使用指南(一)之Intent

    使用Anko Intent帮助器可以添加如下依赖 dependencies { compile "org.jetbrains.anko:anko-commons:$anko_version& ...

  8. es6的几种写法

    语法:1.只有一个参数,可以不用写小括号: var single = a => a; //相当于var single = function(a){return a;}console.log(si ...

  9. 如何利用sqoop将hive数据导入导出数据到mysql

    运行环境  centos 5.6   hadoop  hive sqoop是让hadoop技术支持的clouder公司开发的一个在关系数据库和hdfs,hive之间数据导入导出的一个工具. 上海尚学堂 ...

  10. 站点的rel="alternate"属性

    概述 今天看决战平安京官网源码,突然看到了rel的alternate属性,百度了一下,记录下来,供以后开发时参考,相信对其他人也有用. PC端rel 在pc版网页上,添加指向对应移动版网址的特殊链接r ...