
新的生命周期不再使用以下三个钩子函数
因为 ReactFiber Reconciliation 这个过程有可能暂停然后继续执行,所以挂载和更新之前的生命周期钩子就有可能不执行或执行多次
初始化阶段,由 ReactDOM.render()方法触发初次渲染,会调用以下钩子函数
更新阶段 由组件内部 this.setState()或父组件重新 render 触发
卸载阶段 由 ReactDOM.unmountComponentAtNode()方法触发
初始化阶段
更新阶段
卸载阶段
触发 React 重新渲染
重新渲染 render 会做些什么
React并不是将click事件绑定到了div的真实dom上,而是在document处监听了所有事件,当事件发生并且冒泡到document处的时候,React将事件内容封装并交由真正的处理函数运行。这样不仅减少了内存的消耗,还能在组件挂载销毁时统一订阅和移除事件
除此之外,冒泡到document的事件也不是原生的浏览器事件,而是由react自己实现的合成事件(SyntheticEvent),因此如果不想要事件冒泡的话应该调用 event.preventDefault 方法,而不是 event.stopProppagation 方法

目的
与普通html不同处
执行顺序
原生事件先执行,合成事件后执行,合成事件会冒泡绑定到document上,应避免原生事件和合成事件混用,如果原生事件阻止冒泡,可能会导致合成事件不执行,因为需要冒泡到document上合成事件才执行
Redux 的灵感来源于 Flux 架构和函数式编程原理,状态更新可预测、可跟踪,提倡使用单一存储
单向数据流
三大原则
缺点:
所谓中间件,我们可以理解为拦截器,用于对某些过程进行拦截和处理,且中间件之间能够串联使用;在redux中,我们中间件拦截的是dispath提交到reducer的这个过程,从而增强dispatch的功能
中间件的设计遵循了洋葱模型(Onion Model),即每个Middleware都可以在action被dispatch之前或之后对其进行处理,并且可以选择是否将action传递给下一个middleware,middleware的执行顺序是按照其注册的顺序依次执行的
Zustand 是一个轻量级的状态管理库,它使用简单的API和最小的样板代码来管理状态;基于hooks概念,通过创建一个自定义的Hook来管理状态
支持多个状态切片(slices),每个切片都有自己的状态和操作方法,简单易用、学习成本低、适用于中小型应用程序
import create from 'zustand';
const useCounterStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
})
function Counter() {
const { count, increment, decrement } = useCounterStore();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
)
}Hooks 通常指:系统运行到某一时期时,会调用被注册到该时机的回调函数
// this 使用方式
// 1. 构造函数中绑定this
// 2. render()中绑定this
// 3. 箭头函数
// 4. 使用类的静态属性:原理和第一种差不多,更简洁
class TestThis extends React.Component {
handleClick2;
constructor(props) {
super(props);
this.state = {
num: 1,
title: "react study"
};
this.handleClick2 = this.handleClick1.bind(this);
}
handleClick1() {
this.setState({
num: this.state.num + 1
});
}
handleClick3 = () => {
this.setState({
num: this.state.num + 1
});
};
render() {
return (
<div>
<h2>Hello, {this.state.num}</h2>
<button onClick={() => this.handleClick1()}>but1</button>
<button onClick={this.handleClick2}>but2</button>
<button onClick={this.handleClick1.bind(this)}>but3</button>
<button onClick={this.handleClick3}>but4</button>
</div>
);
}
}
export default TestThis;对比mixins
useState 是 react 自带的一个 hook 函数,他的作用就是用来声明状态变量
性能
默认情况下,只要父组件状态变了,不管子组件一部以来该状态,子组件也会重新渲染
异步更新行为
这个问题的方法是使用 useEffect Hook。useEffect 会在组件渲染后执行,并在组件卸载之前执行清理操作。我们可以使用 useEffect 在组件重新渲染时打印 count 的新值
import React, { useState } from "react";
function TestUseState() {
const [count, setCount] = useState(0);
const [age, setAge] = useState(18);
return (
<div>
<p>{count}</p>
<button
onClick={() => {
setCount(count + 1);
}}
>
计数
</button>
</div>
);
}类似于 componentDidMount 和 componentDidUpdate
我们写的有状态组件,通常会产生很多的副作用(side effect),比如发起 ajax 请求获取数据,添加一些事件监听和取消注册,手动修改 dom 等。我们之前都把这些副作用的函数写在生命周期钩子函数中,比如 componentDidMount,componentDidUpdate;而现在的 useEffect 就相当于这些生命周期的集合体
注意:
// 使用useEffect
useEffect(() => {
document.title = `YOU CLICK ${count} times`;
})
// 不使用useEffect
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}function LayoutEffect() {
const [color, setColor] = useState('red');
useLayoutEffect(() => {
alert(color);
});
useEffect(() => {
console.log('color', color);
});
return (
<>
<div id="myDiv" style={{ background: color }}>颜色</div>
<button onClick={() => setColor('red')}>红</button>
<button onClick={() => setColor('yellow')}>黄</button>
<button onClick={() => setColor('blue')}>蓝</button>
</>
);
}useCallback: 接收一个内联回调函数参数和一个依赖项数组(子组件依赖父组件状态,即子组件会使用到父组件的值),会返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新
把创建函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算
export const useName = () => {
const randomName = useMemo(() => getRandomName(), [])
const [name, setName] = useState(randomName)
return {
name,
setName,
}
}useReducer 和 redux 中的 reducer 很像;useState 内部就是靠 useReducer 来实现的
useState 的替代方案,它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法
在某些场景下,useReducer 会比 useState 更适合,例如:state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于前一个 state
const initialState = 0;
function reducer(state, action) {
console.log("state", state);
switch (action.type) {
case "increment":
return {
...state,
number: state.number + 1
};
case "decrement":
return {
...state,
number: state.number - 1
};
default:
throw new Error("unknown action type");
}
}
function init(initialState) {
return {
number: initialState,
age: 1
};
}
function Counter7() {
const [state, dispatch] = useReducer(reducer, initialState, init);
return (
<div>
Count: {state.number}
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
</div>
);
}
export default Counter7;<MyContext.Provider> 的 value prop 决定<MyContext.Provider>的 value prop 发生改变,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值<MyContext.Consumer><MyContext.Provider>为下层组件提供 contextconst initialState = 0;
function reducer(state, action) {
switch (action.type) {
case "ADD":
return { number: state.number + 1 };
case "MINUS":
return { number: state.number - 1 };
default:
break;
}
}
const CounterContext = createContext("counter");
// 第一种获取 CounterContext 方法:不使用 hook
function SubCounter_one() {
return (
<CounterContext.Consumer>
{value => (
<>
<p>{value.state.number}</p>
<button onClick={() => value.dispatch({ type: "ADD" })}>加</button>
<button onClick={() => value.dispatch({ type: "MINUS" })}>减</button>
</>
)}
</CounterContext.Consumer>
);
}
// 第二种获取 CounterContext 方法:使用 hook
function SubCounter_two() {
const { state, dispatch } = useContext(CounterContext);
return (
<>
<p>{state.number}</p>
<button onClick={() => dispatch({ type: "ADD" })}>加</button>
<button onClick={() => dispatch({ type: "MINUS" })}>减</button>
</>
);
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState, () => ({
number: initialState
}));
return (
<CounterContext.Provider value={{ state, dispatch }}>
<SubCounter_two />
</CounterContext.Provider>
);
}
export default Counter;本质是把可以复用的逻辑抽离出来,变成一个个可随意插拔的插销,哪个组件需要,就插进哪个组件中去
function useFirendState(id) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(isOnline) {
setIsOnline(isOnline);
}
useEffect(() => {
// dosometings
return () => {
// cleanup 清除事件
};
});
return isOnline;
}
// 使用
function FriendStatus(props) {
const isOnline = useFirendState(props.friend.id);
if (isOnline === null) {
return "Loading...";
}
return isOnline ? "Online" : "Offline";
}
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? "green" : "black" }}>{props.friend.name}</li>
);
}主要原理是将渲染过程分成小的工作单元fiber,可以随时中断和恢复,实现可中断的渲染
在React16以前,React更新是通过树的深度优先遍历完成的,遍历是不能中断的,当树的层级深就会产生栈的层级过深,页面渲染速度变慢的问题,为了解决这个问题,引入了fiber
fiber 是一种新的数据结构,是一个链表结构,有三个指针:分别是当前节点的下一个兄弟节点,子节点和兄弟节点
核心概念
更新机制流程
// 受控组件
import React, { useState } from 'react';
function ControlledForm() {
const [value, setValue] = useState('');
const handleChange = (event) => {
setValue(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
console.log('提交的值:', value);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" value={value} onChange={handleChange} />
<button type="submit">提交</button>
</form>
);
}// 非受控组件
import React, { useRef } from 'react';
function UncontrolledForm() {
const inputRef = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
console.log('提交的值:', inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" ref={inputRef} />
<button type="submit">提交</button>
</form>
);
}在React中,refs是React提供给开发者用来访问DOM元素的特殊属性。它允许我们在React组件中直接引用DOM元素或组件实例,并对其进行操作
应用场景
React.forwardRef 会创建一个React组件,这个组件能够将其接收的ref属性转发到其组件树下的另一个组件
import React from 'react';
const MyInput = React.forwardRef((props, ref) => (
<input type="text" ref={ref} {...props} />
));
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
componentDidMount() {
this.inputRef.current.focus();
}
render() {
return (
<div>
<MyInput ref={this.inputRef} />
</div>
);
}
}
export default MyComponent;调用setState函数之后,会将传入的对象合并到当前组件的状态中,并触发组件的重新渲染
在合成事件和生命周期方法中,setState 是异步的,多个 setState 调用会合并为一次批量更新(出于性能原因,多次setState状态变更合并为一次)
第二个参数是一个可选的回调函数,这个回调函数将在组件重新渲染后执行,这个回调函数中你可以拿到更新后的state值
Switch 通常被用来包裹 Route,用于渲染与路径匹配的第一个子 <Route> 或 <Redirect>,它里面不能放其他元素