1.前言
一边学习,一边记笔记,如若有错误望指出,及时改正。
觉得文章可以,那就点个赞吧~
Hooks是react的一个新特性,是在react16.8以上的版本才能使用,
2.Hooks入门
写一个需求:点击按钮,让数字加1,效果图如下:
example.js
import React, { Component } from 'react';
class example extends Component {
constructor(props) {
super(props);
this.state = {
temp:0
};
}
onclick =()=>{
this.setState({
temp:this.state.temp+1
})
}
render() {
return (
<div>
<div>
clickResult is {this.state.temp}
</div>
<button onClick={this.onclick} style={{marginTop:"20px",marginBottom:"20px"}}>click</button>
</div>
);
}
}
export default example;
现在使用hooks编写:
不利用继承component来创建组件,利用function来创建,利用useState来使用及更新状态:
import React, { useState } from 'react';
function Example (){
const [temp, setCount] = useState(0);
return (
<div>
<div>
clickResult is {temp}
</div>
<button onClick={()=>{setCount(temp+1)}} style={{marginTop:"20px",marginBottom:"20px"}}>click this</button>
</div>
);
export default Example;
3.useState
1)如何声明
const [temp,setCount] = useState(0);//在此左边利用数组解构方式,等同于如下:
let _useState = useState(0);
let temp = _useState[0];
let setCount = _useState[1];
2)如何读取
useState这个函数接收的参数是状态的初始值(Initial state),它返回一个数组,这个数组的第0位是当前的状态值,第1位是可以改变状态值的方法函数。
就是利用{xxx}读取
3)如何改变
在上述例子中,利用setCount函数进行改变:
onClick={()=>{setCount(temp+1)}}
setCount这个函数接收的参数是修改过的新状态值。接下来的事情就交给React,他会重新渲染组件。React自动帮助我们记忆了组件的上一次状态值.
4)如何多状态声明
const [ age , setAge ] = useState(18)
const [ sex , setSex ] = useState('男')
const [ work , setWork ] = useState('前端程序员')
return (
<div>
<p>JSPang 今年:{age}岁</p>
<p>性别:{sex}</p>
<p>工作是:{work}</p>
</div>
)
需要注意的是,useState不能在if...else...这样的条件语句中进行调用,必须要按照相同的顺序进行渲染。也就是React Hooks不能出现在条件判断语句中,因为它必须有完全一样的渲染顺序,不允许:
const [ age , setAge ] = useState(18)
if(showSex){
const [ sex , setSex ] = useState('男')
showSex=false
}
const [ work , setWork ] = useState('前端程序员')
这样会报错:
React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render
4.useEffect
useEffect用于替换class创建组件时的生命周期:
利用useEffect实现componentDidmount和componentDidUpdate
先上效果图:
import React, { Component } from 'react';
class Example1 extends Component {
constructor(props) {
super(props);
this.state = { temp:0 }
}
//React组件调用结束时调用
componentDidMount(){
console.log(`ComponentDidMount=>You clicked ${this.state.temp} times`)
}
//组件状态更改调用
componentDidUpdate(){
console.log(`componentDidUpdate=>You clicked ${this.state.temp} times`)
}
render() {
return (
<div>
<p>You clicked {this.state.temp} times</p>
<button onClick={this.addCount.bind(this)}>Chlick me</button>
</div>
);
}
addCount(){
this.setState({temp:this.state.temp+1})
}
}
export default Example1;
再使用useEffect
import React, { useState,useEffect } from 'react';
function Example1 () {
const [temp, setCount] = useState(0);
useEffect(()=>{
console.log(`useEffect=>You clicked ${temp} times`);
})
return (
<div>
<div>
clickResult is {temp}
</div>
<button onClick={()=>{setCount(temp+1)}} style={{marginTop:"20px",marginBottom:"20px"}}>click this</button>
</div>
);
}
export default Example1;
运行可见,useEffect可以替代传统意义上的componentDidMount和componentDidUpdate,但是需要注意的是:
①:一个useEffect就可以代替传统的两个函数(didMount和DidUpdate);
②:useEffect是异步的,定义的函数的执行不会阻碍浏览器视图的更新,若想实时计算页面大小,就没办法同步实现,
利用useEffect实现componentWillUnMount函数
实现componentWillMount功能就是实现卸载功能,先安装路由,实现组件间跳转的组件销毁过程。
1)安装路由
npm install --save react-router-dom
2)搭建最基本的页面布局
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
function Interface() {
return <h2>接口测试</h2>
}
function Group() {
return <h2>组件测试</h2>
}
function Example2() {
const [temp, setCount] = useState(0);
return (
<div>
<div>
clickResult is {temp}
</div>
<button onClick={() => { setCount(temp + 1) }} style={{ marginTop: "20px", marginBottom: "20px" }}>click this</button>
<Router>
<ul>
<li><Link to="/">测试中心</Link></li>
<li><Link to="/group">组件中心</Link></li>
</ul>
<Route path="/" exact component={Interface} />
<Route path="/group" component={Group} />
</Router>
</div>
);
}
export default Example2;
3)利用useEffect实现基本的卸载
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
function Interface() {
//新增
useEffect(() => {
console.log(`Welcome Interface!!!`);
return ()=>{
console.log("ByeBye Interface")
}
})
return <h2>接口测试</h2>
}
function Group() {
//新增
useEffect(() => {
console.log(`Welcome Group!!!`);
return ()=>{
console.log("ByeBye Group")
}
})
return <h2>组件测试</h2>
}
此时你会发现,虽然思想上实现了卸载的意义,但当点击click按钮时,仍然会发现ByeBye...会打印出来,其实每次状态发生变化,useEffect都进行了解绑。那么怎么实现类似于componentWillUnmount的效果呢?
4)利用useEffect的第二个参数实现卸载
也就是在useEffect函数中第一个参数是一个函数,第二个参数是一个数组,数组存放状态值,他的作用就是当状态值发生变化才会进行解绑,如果是一个空数组则表示,只当组件进行改变时进行解绑。代码如下:
function Interface() {
useEffect(() => {
console.log(`Welcome Interface!!!`);
return ()=>{
console.log("ByeBye Interface")
}
},[]) //重点在这行
return <h2>接口测试</h2>
}
function Group() {
useEffect(() => {
console.log(`Welcome Group!!!`);
return ()=>{
console.log("ByeBye Group")
}
},[]) //重点在这行
return <h2>组件测试</h2>
}
5.useContext
用于解决父子组件传值,相当于props。
首先生成一个Context,利用createContext:
const TempContext = createContext();
不过在此之前,需要先引入:
import React, { useState, useEffect, useContext, createContext } from 'React';
接着,如果想给子组件传temp值,要先定义一个提供其Provider:
<TempContext.Provider value={temp}>//里面的value属性的值就是存到了context的公共区域
<Child /> //引入Child子组件
</TempContext.Provider>
在子组件Child.js中:
function Child(){
let temp = useContext(TempContext);
return (<div>传给子组件:{temp}</div>)
}
效果图如下:
import React, { useState, useEffect, createContext, useContext } from 'react';
const TempContext = createContext();
function Child(){
let temp = useContext(TempContext);
return(<div>传给子组件:{temp}</div>)
}
function Example3() {
const [temp, setCount] = useState(0);
useEffect(() => {
console.log(`useEffect=>You clicked ${temp} times`);
})
return (
<div>
<div>
clickResult is {temp}
</div>
<button onClick={() => { setCount(temp + 1) }} style={{ marginTop: "20px", marginBottom: "20px" }}>click this</button>
<TempContext.Provider value ={temp}>
<Child />
</TempContext.Provider>
</div>
);
}
export default Example3;
6.useReducer
Reducer兴起于Redux,但与其无关,reducer是一个函数,可以这么定义:
function tempReducer (state,action){
switch(action.type){
case 'add':
return state+1
case 'sub':
return state-1
default:
return state
}
}
以上代码就是实现一个基本的Reducer,下面具体看一下useReducer
import React, { useState, useEffect, createContext, useContext, useReducer } from 'react';
function Example4() {
// const [temp] = useState(0);
const [temp, dispatch] = useReducer((state, action) => {
switch (action) {
case 'add':
return state + 1
case 'sub':
return state - 1
default:
return state
}
},0) //第二个参数为state的初始值,
return (
<div>
<div>
clickResult is {temp}
</div>
<button onClick={() => { dispatch('add') }} style={{ marginTop: "20px", marginBottom: "20px" }}>click add</button>
<button onClick={() => { dispatch('sub') }} style={{ marginTop: "20px", marginBottom: "20px" }}>click sub</button>
</div>
);
}
export default Example4;
其中useReducer这个方法他有两个参数,第二个参数为默认值,他的返回值有两个,一个是状态,一个是分发器dispatch。dispatch是一个分发器,在redux中通常会这么写:
dispatch(
type:xxx,
value:xxx
)
效果图如下:
7.useContext和useReducer实现Redux
useContext:可访问全局状态。这符合Redux的状态全局化,能统一管理。
useReducer:通过action的传递,更新复杂逻辑的状态,主要是可以实现类似Redux中的Reducer部分,实现业务逻辑的可行性。
下面通过一个例子来详细说明:定义两个按钮来控制字体颜色,其中按钮放在一个组件,文字放在一个组件。效果图:
第一步:编写共享状态:
首先定义一个总得组件:Example6.js
import React, { useReducer } from 'react';
import Example5 from './Example5';
import Examplebtn1 from './Examplebtn1';
import { Color } from './Change'; //引入Color组件
function Example6 (){
return(<div>
<Color> //Color组件用于共享状态
<Example5 />
<Examplebtn1 />
</Color>
</div>)
}
再编写共享组件Color:
import React, { createContext } from 'react';
//首先先创建一个Context
export const ColorContext = createContext();
//再创建Color的共享状态
export const color = props=>{
return(
<ColorContext.Provider value={{color:"black"}}> //设置默认值black
{props.children} //这里面的props.children指的是Color里面的每个组件
</ColorContext.Provider>
)
}
最后编写Example5.js
import React, { useContext } from 'react';
import { ColorContext } from './Change';
function Example5(){
let {color} = useContext(ColorContext);
return (<div style={{color:color}}>颜色为:{color}</div>)
}
export default Example5
第二步:编写usereducer实现状态值更新
定义reducer中的两个参数:
Change.js
import React, { createContext, useReducer } from 'react';
export const UPDAT_COLOR = "UPDAT_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:color,dispatch}} >
{props.children}
</ColorContext.Provider>
)
}
编写按钮组件Examplebtn1
import React, { useContext } from 'react';
import { ColorContext, UPDATE_COLOR } from './Change'
function Examplebtn1(){
const {dispatch} = useContext(ColorContext)
return(
<div>
<button style={{marginTop:"20px",marginRight:"20px"}} onClick={()=>{dispatch({type:UPDATE_COLOR,color:"red"})}}>红色</button>
<button onClick={()=>{dispatch({type:UPDATE_COLOR,color:"yellow"})}}>黄色</button>
</div>
)
}
export default Examplebtn1
代码详见:
github.com/gt3060/reac…