什么是React Hooks?
首先React Hooks是React生态圈里的新特性,它改变了传统react的开发方式。Hooks本质上就是一类特殊的函数,他们可以为你的函数型组件注入一些特殊的功能,主要的改变点如下:
- 用函数的形式代替原本类继承的形式(不再使用class, 让function统一江湖)
- 改变原本复杂的状态操作,使用预函数的形式管理state
- 原本组件分为无状态组件(用函数的形式声明),和有状态组件(用类的形式声明),现在所有的组件都可以用函数声明。
react相关的hooks函数介绍
useState的介绍
useState是react自带的一个hooks函数,它的作用是用来声明状态变量。
const [ count , setCount ] = useState(0);根据这个表达式,简单的介绍一下useState
首先这种写法是是ES6语法中的数组解构。
useState这个函数接收的参数是状态的初始值(这句表达式表示count的初始值是0),它返回一个数组,这个数组的第0位是当前的状态值,第1位是可以改变状态值的方法函数。
// 下边这个小案例,就是实现useState的多状态声明,并实现点击按钮改变变量age的值
import React, { useState } from "react"; // 首先从react中引入useState
function Example1(){ // 使用函数的声明方式定义组件,而非使用继承的方式
// 多状态声明 React是根据useState出现的顺序来确定的
const [age, setAge]=useState(18);
const [sex, setSex]=useState('nv');
const [job, setJob]=useState('前端开发');
return(
<div>
<p>今年{age}岁</p>
<p>性别{sex}</p>
<p>工作{job}</p>
// 直接调用setAge函数,这个函数接收的参数是修改过的新状态值
<button onClick={()=> {setAge(age+1)}}>加一岁</button>
</div>
)
}
export default Example1;
useState的使用时候的注意点:
在进行多状态声明的时候,React是根据useState出现的顺序来找到自己对应的state,
useState不能在if...else...这样的条件语句中进行调用,必须要按照相同的顺序进行渲染,即react hooks不能出现在条件判断语句中。小伙伴在使用的时候要多注意呦~
useEffect的介绍
在使用class定义组件的时候,react会为我们提供生命周期函数,用来处理一些额外的事情,hooks称这些生命周期是<font color="red">副作用</font>(就是和函数的主要业务逻辑关联不大的,需要在某些特定时刻进行执行的函数,比如我们经常使用到的通过Ajax获取远端的数据)。react hooks中也是提供了类似生命周期的副作用,就是使用useEffect来代替。
useEffect的初步认识
import React, { useState , useEffect } from 'react'; // 从react中引入要使用到的useState,useEffect
function Example2(){
const [ count , setCount ] = useState(0);
useEffect(()=>{
console.log(`useEffect=>You clicked ${count} times`)
})
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()=>{setCount(count+1)}}>click me</button>
</div>
)
}
export default Example2;
// 在浏览器中预览,我们会发现在组件第一次被渲染,以及变量发生改变的时候都会触发useEffect的副作用
做一个简单的总结:
- useEffect是可以代替之前react的componentDidMount和componentDidUpdate这两个生命周期
- useEffect是异步的,不会阻碍浏览器的视图更新, 没办法在页面变化的时候进行函数的执行。
而componentDidMount和componentDidUpdate的执行是同步的。
useEffect来实现componentWillUnmount
在组件被卸载的时候我们经常会使用componentWillUnmount生命周期,来进行DOM的移除,定时器的清除,等等,从而做到防止内存泄露。useEffect也是可以实现这个生命周期的,hooks称作这个生命周期为<font color="red">解绑副作用</font>。我们还是用小案例进行解释哈~
为了可以演示出componentWillUnmount的效果,我们需要使用路由跳转,所以我们先进行简单的路由安装
npm install --save react-router-dom
// 相关代码如下,我们主要是为了演示解绑副作用的效果,所以路由跳转用到的组件,暂时不做拆分啦~
import React, { useEffect } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
// 首先这是一个简单的首页组件
function Index(){
useEffect(() => {
console.log('useEffect => 欢迎进入 Index页面');
return () => { // 用返回一个函数的形式进行解绑
console.log('离开Index页面')
}
}, []) // 但是当传空数组[]时,就是当组件将被销毁时才进行解绑,
return (
<h2>我是首页</h2>
)
}
// 这是一个简单的列表组件
function List(){
useEffect(() => {
console.log('useEffect =>欢迎进入List页面')
})
return (
<h2>我是列表页</h2>
)
}
function Example3(){
return(
<div>
<Router>
<ul>
<li> <Link to='/'>首页</Link></li>
<li> <Link to='/list/'>列表页</Link></li>
</ul>
<Route path='/' exact component={Index}/>
<Route path='/list/' component={List}/>
</Router>
</div>
)
}
export default Example3;
简单的总结一下useEffect的解绑副作用
- 它是通过在传入的第一个参数即匿名函数中 <font color="red">通过返回一个函数的形式进行解绑</font>
- 它的第二个参数是一个数组,当传入的是空数组的时候,只有当组件销毁的时候才进行解绑,这也就实现了componentWillUnmount的生命周期函数;当在数组中传入状态对应的变量时,意思是当状态值发生变化时,我们才进行解绑。
useContext的介绍
之前使用的继承的形式来生命组件,我们可以通过constructor中的props进行父子组件传值,当我们使用了函数来定义组件,并没有constructor构造函数了,所以我们需要使用hooks提供的useContext进行跨越组件层级直接传递变量,实现共享。它主要解决的是组件之间值传递的问题。
// 同上一个Demo,这里我们仅仅是为了实现父子组件的传值,为了代码更加好阅读,所以涉及到的组件暂时不做拆分
import React, { useState, createContext, useContext } from 'react'; // 引入相关的hooks函数
const CountContext = createContext(); // ① 核心代码 创建共享的组件(上下文), 用来包裹子组件
function ChildCom(){
let count = useContext(CountContext); // ③ 子组件接受值
return (<h2>{count}</h2>)
}
function Example4(){
const [count, setCount] = useState(0);
return (
<div>
<p>display {count}</p>
<button onClick={() => {setCount(count + 1)}}>click {count}</button>
{/* ② 给子组件共享值 */}
<CountContext.Provider value={count}>
{/* value是要共享的值, 这里包裹的是要分享给的子组件,相当于把count分享给子组件 */}
<ChildCom/>
</CountContext.Provider>
</div>
)
}
export default Example4;
简单总结父子组件传值的步骤
- 使用Test = createContext()创建共享组件上下文,用来包裹子组件,比如这里创建的名字是Test。
- 通过上下文标签包裹子组件,进行数据的共享。
- 子组件通过使用useContext(Test)获取父组件传递过来的值。
userReducer的介绍
我们先简单的讲一下Redux中的reducer,reducer其实就是一个普通的函数,这个函数接受2个参数,一个是状态,一个是控制业务逻辑的的判断参数,更通俗一点就是,一个是状态,一个是控制状态。userReducer和reducer很类似,我们通过一个实现增加和减少数据的案例来演示一下它的使用~
useReducer更新逻辑状态的示例
import React, { useReducer } from 'react'; // 引入hooks的useReducer函数
function ReducerDemo(){
// 传入两个参数 ① 相关执行的方法 ② 变量的初始值
// 返回两个参数 ① 通过相关业务逻辑后返回的值这里是count
② 第二个参数是个派发器,通过传递action走不同的业务逻辑
const [count, dispatch] = useReducer((state, action) => {// 这个函数的参数 一个是状态,一个是如何控制状态
switch(action){
case 'add':
return state + 1;
case 'sub':
return state - 1;
default:
return state;
}
}, 0)
return (
<div>
<h2>现在的分数是{count}</h2>
<button onClick={() => {dispatch('add')}}>Increment</button>
<button onClick={() => {dispatch('sub')}}>Descrement</button>
</div>
)
}
export default ReducerDemo;
useReducer与useContext实现Redux的示例
这里主要想通过点击按钮来实现页面文字颜色切换的功能。这里的一个核心组件就是用来管理颜色的和处理颜色的Color.js组件。本次示例只要使用的组件有:管理颜色的Color.js组件;操作颜色变化的Button.js组件;显示文字的ShowText组件;以及他们的父组件Combine.js。具体逻辑请看一下代码~
// 核心组件Color组件(拥有共享状态和处理业务能力的组件)
import React, {createContext, useReducer} from 'react';
export const ColorContext = createContext({}); // 将属性color共享出去
// 封装useReducer的中要执行的相关方法
export const UPDATE_COLOR = 'UPDATE_COLOR';
const reducer = (state, action) => {
switch(action.type){
case UPDATE_COLOR:
return action.color;
default:
return state;
}
}
export const Color = props => {
const [color, dispatch] = useReducer(reducer, 'blue');
return (
<ColorContext.Provider value={{color, dispatch}}> //共享出去的状态变成了color和dispatch
{props.children} // 使用 {props.children}来显示对应的子组件
</ColorContext.Provider>
)
}
// 切换颜色的Button组件
import React, {useContext} from 'react';
import { ColorContext, UPDATE_COLOR } from './color';
function Buttons(){
const {dispatch} = useContext(ColorContext); // 使用color组件,共享出来的
return (
<div>
<button onClick={() => {dispatch({type: UPDATE_COLOR, color: 'red'})}}>红色</button>
<button onClick={() => {dispatch({type: UPDATE_COLOR, color: 'yellow'})}}>黄色</button>
</div>
)
}
export default Buttons;
// 显示文字的区域
import React, {useContext} from 'react';
import { ColorContext } from './color';
function ShowText(){
const {color} = useContext(ColorContext); // 也是使用共享出来的颜色变量
return (
<div style={{color: color}}>
字体颜色为{color}
</div>
)
}
export default ShowText;
// 以上子组件共同的父组件
import React from 'react';
import ShowText from './showText';
import Buttons from './buttons';
import { Color } from './color';
function Combine(){
return (
<div>
<Color>
// ShowText和Buttons当做是Color的子组件,ShowText共享到color属性,Buttons操作颜色的变化
<ShowText/>
<Buttons/>
</Color>
</div>
)
}
export default Combine;
这种useReducer与useContext实现Redux的效果,比之前的Redux简单些,但是还是有一定难度的,希望这个小案例可以帮助大家更好的理解useReducer和useContext吧~
useMemo的介绍
之前我们在介绍react生命周期的时候,有提到过shouldComponentUpdate,我们可以在组件更新之前,在这个生命周期中对比状态,只有真正需要更新的时候才更新,可是hooks中的useEffect没有这个功能,所以会导致只要父组件更新,子组件里的方法都会执行一次,严重影响性能。 这个时候hooks给我们提供了useMemo,它主要用来解决使用React hooks产生的无用渲染的性能问题。
// 这个案例主要通过在父组件中点击不同的按钮,显示当前的时间戳,从而来验证父组件变化是否会影响子组件的变化
import React, { useState, useMemo } from 'react'; // 首先引入useMemo函数
function MemoDemo(){
const [reactTime, setReactTime] = useState('学习React');
const [ngTime, setNgTime] = useState('学习Angular');
return (
<>
<button onClick={() => {setReactTime(new Date().getTime())}}>React</button>
<button onClick={() => {setNgTime(new Date().getTime()+'Angular is studying')}}>Angular</button>
<ChildComponent name={reactTime}> { ngTime } </ChildComponent>
</>
)
}
// 新建一个子组件
function ChildComponent({name, time}){
// 如果不使用useMemo,只要父组件发生变化,这个函数都会执行的
function changeReactTime(){
console.log('she is coming, React');
return name+',React is coming';
}
//由于传入的name是reactTime,所以只有reactTime改变的时候,子组件的这个方法才会执行。
const actionReact = useMemo(() => changeReactTime(name), [name]);
return (
<>
<div>{actionReact}</div>
<div>{time}</div>
</>
)
}
export default MemoDemo;
总结: useMemo函数需要传入两个参数,第一个是本来就要执行的函数,第二个是决定这个函数是否要执行的变量~ 只有第二个参数匹配成功,才会执行第一个参数中的函数。
useCallback的介绍
之前我们介绍到useMemo是为了缓存变量,这里我们介绍的usecallback是为了缓存方法。我们可以通过自定义hooks函数来使用一下useCallback,我们可以通过自定义hooks函数来实现一个获取窗口大小的函数。需要注意的一点是自定义的hooks函数需要使用use开头。(这块内容我并没有深入的了解,所以如有不足,请大家多多包涵哈~)
import React, { useState ,useEffect ,useCallback } from 'react';
// 自定的useWinSiz hooks函数
function useWinSize(){
const [ size , setSize] = useState({
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight
})
const onResize = useCallback(()=>{
setSize({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
})
},[])
useEffect(()=>{
window.addEventListener('resize',onResize)
return ()=>{
window.removeEventListener('resize',onResize)
}
},[])
return size;
}
function CallBackDemo(){
const size = useWinSize()
return (
<div>页面Size:{size.width}x{size.height}</div>
)
}
export default CallBackDemo;
总结
react hooks就介绍到这里啦,其中这些案例我也就简单的敲过一遍,由于没有实质项目的编写经验,所以肯定还是有很多不足的地方,假如介绍的内容有错误的地方,还请大家多多指正哈~