react hooks在react16.8版本以后,推出的一组函数式的api.专门用于函数组件,使函数组件也能像类组件一样使用state和生命周期等一些特性。
1.useState
useState可以使函数组件处理数据的状态。它只能放在函数组件中,不可以用在判断和循环语句中。
let [state,setState] = useState(initialState);
useState接收一个state初始值,返回这个state和设置state的函数;设置state的函数不能自动合并state中的数据,需要手动合并。
代码示例:
1.未封装版form表单收集数据
import { useState } from 'react';
// 使用函数实现表单数据收集
// 不需要访问this
// 代码更简洁
function Form() {
let [ email, setEmail ] = useState('');
let [ password, setPassword ] = useState('');
return (
<div>
<h1>react hooks</h1>
<div>
<input type="text" name="email" placeholder="输入邮箱" value={email} onChange={e => setEmail(e.target.value)} />
</div>
<div>
<input type="password" name="password" placeholder="输入密码" value={password} onChange={e => setPassword(e.target.value)} />
</div>
<div>
<button onClick={e => console.log('Form 数据:',email,password)}>提交</button>
</div>
</div>
);
}
export default Form;
2.自定义hook封装版form表单收集数据
//hooks/useForm.js
import { useState } from 'react';
// 收集表单数据的自定义hooks
export default function useForm(initialVal){
let [ values, setValues ] = useState(initialVal);
// 返回一个数组
return [
values,
// 箭头函数方法
e => {
//useState不能自动合并state的数据,以下通过解构,新值覆盖的方式来合并
// 解构原先的对象,从e.target中收取最新的一个[name:value],覆盖原先的[name:value],此操作将数据进行合并
setValues({
...values,
[e.target.name]: e.target.value
})
}
]
}
/src/FormComp.js
import useForm from './hooks/useForm';
// 使用函数实现表单数据收集
// 不需要访问this
// 代码更简洁
function FormComp() {
let [ values, handleChange ] = useForm({ email: '', password: '' });
return (
<div>
<h1>react hooks</h1>
<div>
<input type="text" name="email" placeholder="输入邮箱" value={values.email} onChange={handleChange} />
</div>
<div>
<input type="password" name="password" placeholder="输入密码" value={values.password} onChange={handleChange} />
</div>
<div>
<button onClick={e => console.log('Form 数据:',values.email,values.password)}>提交</button>
</div>
</div>
);
}
export default FormComp;
2.useEffect
useEffect的四个用法
- 注册一个生命周期didMount和didUpdate
- 增加清除操作(返回一个清除函数,在该函数中清除);比如清除定时器,绑定的事件等等,类似于在willUnmount生命周期中执行一些解除的操作
- 只注册一次didMount和willUnmount(给useEffect的第二个参数传入空数组);常用于初次发送异步请求此类操作
- 性能优化,在某个条件下更新
代码示例:
//用法一 注册一个生命周期didMount和didUpdate
import { useEffect,useState } from 'react';
export default function LearnEffect(){
let [ count,setCount ] = useState(0);
//此函数会在didMount和didUpdate时执行
useEffect(() => {
console.log('render: '+ count);
console.log('didMount&&didUpdate');
});
return (
<div>
<h1>useEffect 用法</h1>
<p>{count}<p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}
//用法二 增加清除操作(返回一个清除函数,在该函数中清除);比如清除定时器,绑定的事件等等,类似于在willUnmount生命周期中执行一些解除的操作
useEffect(() => {
const subscription = props.source.subscribe();
//返回清除函数
return function cleanup(){
//清除订阅
subscription.unsubscribe();
}
})
//用法三 只注册一次didMount和willUnmount(给useEffect的第二个参数传入空数组);常用于初次发送异步请求此类操作
//形成闭包,无法获取最新的state,setState 需要传递函数
//当传入空数组时,效果相当于拥有了didMount和willUnmount两个生命周期
useEffect(() => {
const subscription = props.source.subscribe();
//返回清除函数
return function cleanup(){
//清除订阅
subscription.unsubscribe();
}
},[])
//用法四 性能优化,在某个条件下更新
useEffect(() => {
const subscription = props.source.subscribe();
//返回清除函数
return function cleanup(){
//清除订阅
subscription.unsubscribe();
}
//传入非空数组,则在此条件下进行渲染更新
},[props.id])
3.useRef
代替类组件中的React.createRef() ,在函数组件中使用的ref.
import { useRef } from 'react';
export default function UseRefTest(){
// 声明一个元素,一开始不知道指向哪个标签,先标识为null
const element = useRef(null);
function handleClick(){
console.log('获取input的值:', element.current.value);
}
return (
<div>
<input type="text" ref={element} />
<button onClick={handleClick} >获取input的值</button>
</div>
)
}
4.useContext
通过createContext来创建上下文空间,Provider组件来传递值;通过以下两种方式来获取值:
- Consumer组件中解构获取
- useContext(上下文) 获取
import React, { useState, useContext, createContext } from 'react';
// 创建上下文空间
const NumContext = createContext();
console.log('NumContext:', NumContext);
function Son(){
// const { num } = useContext(NumContext);
// return <div>啦啦啦{num}</div>
return (
// useContext 消费者
<NumContext.Consumer>
{/* 内部可以接收到context的数据,通过解构使用 */}
{
({num}) => <div>呵呵呵{num}</div>
}
</NumContext.Consumer>
)
}
function Btn(){
return (
<NumContext.Consumer>
{
({num,setNum}) => <button onClick={ () => { setNum(num + 1) }} >累加</button>
}
</NumContext.Consumer>
)
}
export default function UseContextTest(){
const [num,setNum] = useState(0);
return (
<div>
{/*
*上下文空间提供器
单参传递,value={变量}
多参处传递,value={ obj } 传递对象
*/}
<NumContext.Provider value={{ num,setNum }}>
<Son />
<Btn />
</NumContext.Provider>
</div>
)
}
tips:跨文件时如何传递?
5.useReducer
在函数组件中使用的reducer
import React, { useReducer } from 'react';
// 定义一个关于num的reducer
function NumReducer(state, action){
let newState = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'addNum':
newState.num += action.value;
break;
default:
break;
}
return newState;
}
export default function UseReducerTest(){
// useReducer:传入手写reducer方法和原始state返回新state和dispatch进行使用
const [state, dispatch] = useReducer(NumReducer, { num: 0});
return (
<div>
<div>嘻嘻嘻{state.num}</div>
<button onClick={ () => dispatch({ type: 'addNum', value: 1 }) } >累加</button>
</div>
)
}
延伸:
使用useReducer和useContext来实现简易react-redux
import React, { useReducer, useContext, createContext } from 'react';
// 1-创建上下文空间
const NumContext = createContext();
function NumReducer(state, action){
let newState = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'addNum':
newState.num += action.value;
break;
default:
break;
}
return newState;
}
function Son(){
return (
// 使用消费者
<NumContext.Consumer>
{
({state}) => <div>嘿嘿嘿{state.num}</div>
}
</NumContext.Consumer>
)
}
function Btn(){
return (
// 使用消费者
<NumContext.Consumer>
{
({dispatch}) => <button onClick={ () => dispatch({ type: 'addNum', value: 1 }) } >累加</button>
}
</NumContext.Consumer>
)
}
// useContext搭配useReducer实现react-redux
export default function UseReducerTest(){
// useReducer:传入手写reducer方法和原始state返回新state和dispatch进行使用
const [state, dispatch] = useReducer(NumReducer, { num: 0});
return (
<div>
{/* 2-上下文空间传参,传参对象为useReducer的返参 */}
{/* 使用提供者 */}
<NumContext.Provider value={{state, dispatch}}>
<Son />
<Btn />
</NumContext.Provider>
</div>
)
}