类组件需要去继承React.Component并创建render函数来返回react元素
有setState和生命周期(dismount, didupdate, willunmount等)对状态进行管理
缺点:
逻辑复杂的组件难以开发与维护,每个生命周期函数中可能会包含着各种互不相关的逻辑在里面。比如componentDidMount生命周期,需要获取后端数据, 也会需要添加事件监听, 将两个不相关的函数放在了同一个地方, 当项目变得庞大时,容易变得让人难以理解。
-> 1ClassComponent.js
import React from 'react';
class ClassComponent extends React.Component {
// 初始化类组件的 state
state = {
count: 0
};
// 编写生命周期方法 didMount
componentDidMount() {
// ...业务逻辑
this.setState({
count: this.state.count + 1
})
}
// 编写自定义的实例方法
addNumber = () => {
// 更新 state
const newCount = this.state.count + 1;
this.setState({
count: newCount
});
};
// 编写生命周期方法 render
render() {
return (
<div>
<h2>类组件写法</h2>
<div className="demoClass">
<div style={{ marginBottom: 20 }}>you clicked {this.state.count} times</div>
<button onClick={() => this.addNumber()}>click me</button>
</div></div>
);
}
}
export default ClassComponent;
函数式组件 本质上是一个常规函数(纯函数),接受一个参数props, 并返回一个reactElement
缺点
import { useState } from 'react';
// 函数式写法
// useState
function Example() {
// number 为state读取值, setNumber为派发更新的函数
const [number, setNumber] = useState(0); // 0为初始值
// // 入参可以是函数, 处理复杂的逻辑 返回值是初始值
// const flag = 1
// const [number, setNumber] = useState(() => {
// return flag === 1 ? 10 : 100
// })
return (
<div>
<h2>函数式写法 / useState</h2>
<p>You clicked {number} times</p>
<button onClick={() => {
setNumber(number + 1)
console.log('number', number) // number的值不是即时改变的, 当下次下上文执行式state才会改变
}}>click me</button>
</div>
)
}
export default Example;
在react类组件(class)写法中,有setState和生命周期对状态进行管理,但是在函数组件中不存在这些,故引入hooks(版本:>=16.8),使开发者在非class的情况下使用更多react特性。
入参 具体值或者一个函数
返回值 数组 第一项是state值, 第二项负责派发数据更新, 组件渲染
// hooks
const [number, setNumber] useState(0)
// 等价于class
this.state.number
this.setState({
number: newNumber
})
-> 2UseStateDemo.js
useRef相当于在函数式组件中添加了一个实例对象,通过refName.current
保证获取到的数据肯定是最新的,类似于类组件的this
返回的ref对象在组件的整个生命周期内保持不变
当更新current值时并不会re-render,这是与useState不同的地方
应用场景:获取元素, 缓存数据
// useRef
function Example() {
const inputRef = useRef(null); // 入参为初始值
// 操作当前dom元素
const focusInput = () => {
console.log('focus current dom');
inputRef.current.focus();
}
// 获取表单元素的值
const handleSubmit = () => {
console.log('submit content', inputRef.current.value)
}
return (
<div>
<h2>useRef</h2>
<input type='text' ref={inputRef} />
<button onClick={() => { focusInput() }}>focus me</button>
<button onClick={() => { handleSubmit() }}>submit it</button>
</div>
)
}
import { useState, useEffect, useRef } from 'react';
// 模拟数据交互 从后台获取信息
function getUserInfo(name) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
age: 18,
name,
})
}, 300)
})
}
// useEffect
function Example({ name }) {
const [userMessage, setUserMessage] = useState({})
const firstRenderRef = useRef(true);
const [ number , setNumber ] = useState(0);
useEffect(() => {
// StrictMode模式会调用两次, 所以使用ref保证初始化时仅调用一次
if(firstRenderRef.current){
firstRenderRef.current = false;
return;
}
// 副作用函数中执行具体的请求逻辑
getUserInfo(name).then(res => {
console.log('UseEffect:', res);
setUserMessage(res)
})
// async/await写法
// const getUserInfoAsync = async () => {
// const result = await getUserInfo(name)
// setUserMessage(result);
// console.log('UseEffect Async', result);
// };
// getUserInfoAsync();
// 定义一个计时器
// const timer = setInterval(()=> console.log('number '+number), 2000)
// 清除副作用函数 cleanup
return function(){
// console.log('stop');
// clearInterval(timer)
}
}, [name, number]) // 只有在props->name / state->number改变的时候会触发useEffect函数的执行
return (
<div>
<h2>useEffect</h2>
<div>name {userMessage.name}</div>
<div>age {userMessage.age}</div>
<div>number {number}</div>
<button onClick={() => {
setNumber(number+1)
}}>click me</button>
</div>
)
}
export default Example;
作用渲染更新之前的useEffect
方便组件间传值, 用来获取父组件传递过来的context值,当前值即最近父组件Provider的value
import { useState, useContext, createContext } from 'react';
//创建context 它返回一个具有两个值的对象 {Provider, Consumer}
const nameContext = createContext(null);
const colorContext = createContext(null);
// useContext
function Example() {
return (
// colorContext.Provider 创建一个局部作用域 为所有子孙提供value值
<colorContext.Provider value={'red'}>
<nameContext.Provider value={'BBB'}>
<h2>useContext</h2>
<ChildA />
<ChildB />
</nameContext.Provider>
</colorContext.Provider>
)
// // 将值和修改值的方式都传递给子组件
// const [color, setColor] = useState("red");
// return (
//
// useContext
//
//
// )
}
function ChildA() {
//使用Consumer从上下文获取value
return (
<colorContext.Consumer>
{
(color) => (
<nameContext.Consumer>
{name => (
<div>not useContext: my name is {name} and color is {color}</div>
)}
</nameContext.Consumer>
)
}
</colorContext.Consumer>
)
}
function ChildB() {
//调用useContext,传入createContext获取的上下文对象。
const name = useContext(nameContext);
const color = useContext(colorContext);
return (
<div>
useContext: my name is {name} and color is {color}
</div>
)
}
function ChildC() {
const { color, setColor } = useContext(colorContext);
return (
<colorContext.Provider value={{ color, setColor }}>
<div>
<p>{color}</p>
<button onClick={() => (setColor('green'))}>修改颜色</button>
</div>
</colorContext.Provider>
)
}
export default Example;
类似redux, 它接收一个形如 (state, action) => newState 的 reducer
,并返回当前的 state 以及与其配套的 dispatch
方法
入参
函数 可以视为reducer, 包括state和action, 返回值是根据action的不同而改变后的state
state的初始值
出参
import { useReducer } from 'react';
const initialState = { count: 0 };
// 第二个参数:state的reducer处理函数
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Example() {
// 返回值:最新的state和dispatch函数
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h2>useReducer</h2>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
export default Example;
执行函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算
缓存的结果是回调函数中return回来的值
import { useState, useMemo, useEffect } from 'react';
function Example() {
// 这里维护了两个state,
// 可以看到getNum的计算仅仅跟count有`关,
// 但是现在无论是count还是val变化,都会导致getNum重新计算,
// 所以这里我们希望val修改的时候,不需要再次计算`,这种情况下我们可以使用useMemo
const [count, setCount] = useState(1);
const [val, setValue] = useState('');
// // 不使用useMemo的写法
const getNum = () => {
console.log('getNumber');
return count
}
// // useEffect 只是限制了触发条件,在进行DOM的操作(setState)后仍然会触发getNum函数
// useEffect(()=>{
// console.log('useEffect:', count);
// },[count])
// 使用useMemo
// const getNum = useMemo(() => {
// console.log('getNumber');
// return count
// }, [count])
return
useMemo
result {getNum()}
setValue(event.target.value)} />
;
}
export default Example;
useCallback与useMemo类似, 第一个参数是一个回调函数, 第二个参数是依赖的数据
不同的地方在于useCallback 缓存的结果是函数
父组件传递一个函数给子组件的时候,由于父组件的更新会导致该函数重新生成从而传递给子组件的函数引用发生了变化,这就会导致子组件也会更新,而很多时候子组件的更新是没必要的,所以我们可以通过useCallback
来缓存该函数,然后传递给子组件
import { memo, useCallback, useState } from 'react'
// 父组件传递一个函数给子组件的时候,由于父组件的更新会导致该函数重新生成
// 从而传递给子组件的函数引用发生了变化,这就会导致子组件也会更新.
// 而很多时候子组件的更新是没必要的,所以我们可以通过`useCallback`来缓存该函数,然后传递给子组件
const DemoChildren = memo(function ({ getInfo }) {
console.log('子组件更新');
return 总和:{getInfo()}
})
const Example = ({ id }) => {
const [number, setNumber] = useState(1);
const [value, setValue] = useState(10);
// // 不使用useCallback
const getInfo = ()=> {
return number;
}
// 使用useCallback
// const getInfo = useCallback(() => {
// return number;
// }, [number]);
return
useCallback
}
export default Example