更轻量级的集中式状态管理工具1. 示例import { create } from 'zustand' const useStore = create((set) => { return { // 状态数据 count: 0, // 修改状态数据的方法 inc: () => { // 修改原数据 set((state) => ({ count: state.count + 1 })) // 直接修改 // set({ count: 100 }) } } }) function App() { const { count, inc } = useStore() return ( {count} + 1 ) } export default App 2. 异步方法import { useEffect } from 'react' import { create } from 'zustand' const useStore = create((set) => { return { // 状态数据 count: 0, channelList: [], // 修改状态数据的方法 inc: () => { // 修改原数据 set((state) => ({ count: state.count + 1 })) // 直接修改 // set({ count: 100 }) }, getChannelList: async () => { const res = await fetch('http://geek.itheima.net/v1_0/channels') const resJson = await res.json() console.log(resJson) set({ channelList: resJson.data.channels }) } } }) function App() { const { count, inc, channelList, getChannelList } = useStore() useEffect(() => { getChannelList() }, [getChannelList]) return ( {count} + 1 {channelList.map((item) => ( {item.name} ))} ) } export default App 3. 切片模式当单个 store 较大时，可以用切片模式进行模块的拆分组合import { useEffect } from 'react' import { create } from 'zustand' const createCountStore = (set) => { return { count: 0, inc: () => { set((state) => ({ count: state.count + 1 })) } } } const createChannelStore = (set) => { return { channelList: [], getChannelList: async () => { const res = await fetch('http://geek.itheima.net/v1_0/channels') const resJson = await res.json() console.log(resJson) set({ channelList: resJson.data.channels }) } } } const useStore = create((...a) => { return { ...createCountStore(...a), ...createChannelStore(...a) } }) function App() { const { count, inc, channelList, getChannelList } = useStore() useEffect(() => { getChannelList() }, [getChannelList]) return ( {count} + 1 {channelList.map((item) => ( {item.name} ))} ) } export default App
1. 概念通过 js 中的类来组织代码2. 实现方式（1）通过类属性 state 定义状态数据（2）通过 setState 方法修改状态数据（3）通过 render 函数返回要渲染的 UI3. 示例import { Component } from 'react' class Counter extends Component { state = { count: 0 } setCount = () => { this.setState({ count: this.state.count + 1 }) } render() { return {this.state.count} } } function App() { return ( <Counter /> ) } export default App 4. 生命周期函数示例（以 componentDidMount, componentWillUnmount 为例）：import { Component, useState } from 'react' class Son extends Component { // 组件渲染完毕执行一次 componentDidMount() { console.log('组件渲染完毕') this.timer = setInterval(() => { console.log('执行定时器') }, 1000) } // 组件卸载之后执行（常用于清除副作用） componentWillUnmount() { console.log('子组件被卸载') clearInterval(this.timer) } render() { return Son Component } } function App() { const [show, setShow] = useState(true) return ( {show && <Son />} setShow(false)}>unmount son component ) } export default App 5. 组件通信概念：与常规的组件通信思路一致（父传子、子传父、兄弟组件间通信）（1）父传子import { Component } from 'react' class Parent extends Component { state = { msg: '父组件的数据' } render() { return ( Parent Component <Son msg={this.state.msg} /> ) } } class Son extends Component { render() { return ( Son Component {this.props.msg} ) } } function App() { return ( <Parent /> ) } export default App （2）子传父import { Component } from 'react' class Parent extends Component { state = { msg: '父组件的数据' } getSonMsg = (sonMsg) => { console.log('子组件传递的数据：', sonMsg) } render() { return ( Parent Component <Son msg={this.state.msg} getSonMsg={this.getSonMsg} /> ) } } class Son extends Component { render() { return ( Son Component {this.props.msg} this.props.getSonMsg('孙笑川')}> 传递数据给父组件 ) } } function App() { return ( <Parent /> ) } export default App （3）兄弟组件通信import { Component } from 'react' class Parent extends Component { state = { msg: '父组件的数据', msg1: '' } setMsg1 = (val) => { this.setState({ msg1: val }) } getSonMsg = (sonMsg) => { console.log('子组件传递的数据：', sonMsg) } render() { return ( Parent Component <Son msg={this.state.msg} getSonMsg={this.getSonMsg} setMsg={this.setMsg1} /> <Daughter msg={this.state.msg} brotherMsg={this.state.msg1} /> ) } } class Son extends Component { render() { return ( Son Component {this.props.msg} this.props.getSonMsg('孙笑川')}> 传递数据给父组件 this.props.setMsg('药水哥')}> 传递数据给兄弟组件 ) } } class Daughter extends Component { render() { return ( Daughter Component 父组件传递的数据：{this.props.msg} 兄弟组件传递的数据：{this.props.brotherMsg} ) } } function App() { return ( <Parent /> ) } export default App
作用：通过 ref 讲子组件的方法暴露给父组件import { forwardRef, useImperativeHandle, useRef } from 'react' const Son = forwardRef((props, ref) => { const inputRef = useRef(null) const inputHandle = () => { inputRef.current.focus() } useImperativeHandle(ref, () => { return { inputHandle } }) return }) function App() { const sonRef = useRef(null) const sonInputFocusHandle = () => { console.log(sonRef) sonRef.current.inputHandle() } return ( <Son ref={sonRef} /> focus ) } export default App
作用：使用 ref 暴露组件给父节点import { forwardRef, useRef } from 'react' // function Son() { // return // } const Son = forwardRef((props, ref) => { return }) function App() { const sonRef = useRef(null) const sonInputFocus = () => { console.log(sonRef) sonRef.current.focus() } return ( <Son ref={sonRef} /> focus ) } export default App
作用：在组件多次重新渲染时缓存函数import { memo, useCallback, useState } from 'react' const MemoInput = memo(function Input({ onChange }) { console.log('执行子组件重新渲染') return onChange(e.target.value)} /> }) function App() { const changeHandle = useCallback((val) => { console.log(val) }, []) const [count, setCount] = useState(0) return ( <MemoInput onChange={changeHandle} /> setCount(count + 1)}>{count} ) } export default App
作用：允许组件在 Props 没有改变的情况下跳过渲染注：React 组件渲染机制，只要父组件重新渲染，子组件也会重新渲染1. 示例import { memo, useState } from 'react' // function Son() { // console.log('执行子组件渲染') // return Son Component // } // props 发生变化时才重新渲染子组件 const MemoSon = memo(function Son() { console.log('执行子组件渲染') return Son Component }) function App() { const [count, setCount] = useState(0) return ( count: {count} setCount(count + 1)}>count + 1 <MemoSon /> ) } export default App 2. props 的比较机制在使用 memo 缓存组件后，React 会对每一个 prop 使用 Object.is 方法比较心值和旧值prop 是简单（基本）类型Object.is(1, 1) ---> true 无变化prop 是引用类型（对象/数组）Object.is([], []) ---> false 有变化（即：React 不关心内容，只关心引用是否发生变化）import { memo, useMemo, useState } from 'react' const MemoSon = memo(function Son({ count, list }) { console.log('执行子组件渲染') return Son Component }) function App() { const [count, setCount] = useState(0) const index = 100 const list = [1, 2, 3] // 保持引用稳定 const memoList = useMemo(() => { return [1, 2, 3] }, []) return ( count: {count} setCount(count + 1)}>count + 1 {/* 子组件 prop 发生变化（count累加），执行重新渲染 */} {/* <MemoSon count={count} /> */} {/* 子组件 prop 无变化（传递的 count 是固定的），不执行重新渲染 */} {/* <MemoSon count={index} /> */} {/* count 累加变化时，父组件重新渲染，list 也会重新执行声明（即子组件 prop 参数的引用发生了变化，执行重新渲染） */} {/* <MemoSon list={list} /> */} {/* memo 保持引用稳定 */} <MemoSon list={memoList} /> ) } export default App
作用：在组件每次重新渲染的时候缓存计算的结果import { useMemo, useState } from 'react' function fib(n) { console.log('执行斐波那契计算函数') if (n < 3) { return 1 } return fib(n - 2) + fib(n - 1) } function App() { const [count1, setCount1] = useState(0) const [count2, setCount2] = useState(0) // 只有count1变化时，才重新执行斐波那契计算函数 // useMemo 在计算密集型场景使用 const result = useMemo(() => { return fib(count1) }, [count1]) console.log('组件执行重新渲染') return ( count1: {count1} setCount1(count1 + 1)}>count1 + 1 count2: {count2} setCount2(count2 + 1)}>count2 + 1 result: {result} ) } export default App
作用：和 useState 类似，用于管理相对复杂的状态数据基础用法：定义 reducer 函数组件中调用 useReducer，并传入 reducer 函数和初始值通过 dispatch 函数分派一个 action 对象（通知 reducer 要返回哪个新状态并渲染 UI）import { useReducer } from 'react' function reducer(state, action) { switch (action.type) { case 'INC': return state + 1 case 'DEC': return state - 1 case 'SET': return action.payload default: return state } } function App() { const [state, dispatch] = useReducer(reducer, 0) return {state} {dispatch({type: 'INC'})}}>+ {dispatch({type: 'DEC'})}}>- {dispatch({type: 'SET', payload: 100})}}>SET } export default App
1. 示例https://github.com/suaxi/develop-relevant-notes/tree/main/front-end/React18/code/react-router-demo2. 路由导航（1）声明式路由导航在模板中通过 <Link /> 组件描述要去哪里const Login = () => { return ( 登录 {/* 声明式导航 */} 跳转到文章页面 ) } export default Login 语法说明：通过给组件的 to 属性指定要跳转的路由path，组件会被渲染为浏览器支持的 a 链接，如需要传参时，直接通过字符串拼接的形式即可（2）编程式导航指通过 useNavigate 钩子函数得到导航方法，然后通过调用方法以命令式的形式进行路由跳转import { Link, useNavigate } from 'react-router-dom' const Login = () => { const naigate = useNavigate() return ( 登录 {/* 声明式导航 */} 跳转到文章页面 {/* 编程式导航 */} naigate('/article')}>跳转到文章页面 ) } export default Login 语法说明：通过调用 navigate 方法传入路由 path 进行跳转3. 路由传参（1）searchParams 传参naigate('/article?name="孙笑川"&age=33')const [params] = useSearchParams() const name = params.get('name') const age = params.get('age')（2）params 传参naigate('/article/33')// 绑定参数 const router = createBrowserRouter([ { path: '/article/:name/:age', element: <Article /> } ])const params = useParams() const name = params.name const age = params.age4. 嵌套路由在一级路由中又嵌套了其他路由，这种关系就叫做嵌套路由（1）实现步骤使用 children 属性配置路由嵌套关系使用 <Outlet /> 组件配置二级路由渲染位置{ path: '/', element: <Layout />, children: [ { path: 'board', element: <Board /> }, { path: 'about', element: <About /> } ] }const Layout = () => { return ( 一级路由 Layout 看板 关于 {/* 二级路由出口 */} <Outlet /> ) }（2）默认二级路由一般情况下，访问一级路由时，是不会渲染二级路由的，当去掉二级路由的 path，添加 index: true 属性时，访问一级路由时，会默认渲染出二级路由{ path: '/', element: <Layout />, children: [ { // path: 'board', index: true, element: <Board /> }, { path: 'about', element: <About /> } ] }5. 404 路由当用户输入的 url 在整个路由配置中都找不到对应的 path 时，可以使用 404 兜底组件改善用户体验（1）实现步骤404 组件在路由表数组的末尾，以 * 号作为路由 path 进行配置const NotFound = () => { return ( 404 Not Found ) } export default NotFound { path: '*', element: <NotFound /> }6. 两种路由模式路由模式url原理是否需要后端支持historyurl/layouthistory 对象 + pushState 事件是hashusr/#/layout监听 hashChange 事件否使用方式：创建路由时，通过切换 createBrowserRouter、createHashRouter Api 即可
1. 概念Redux 是 React 最常用的集中式状态管理工具，与 Vue 中的 Vuex、Pina 类似，可以独立于框架运行2. 快速上手计数器 demo定义 reducer 函数使用 createStore 方法传入 reducer 函数，生成 store 实例对象使用 store 实例的 subscribe 方法订阅数据的变化使用 store 实例的 dispatch 方法提交 action 对象，以此触发数据的变化（告诉 reducer 对数据的修改）使用 store 实例的 getState 方法获取最新的状态数据，更新视图 Redux Quick Start Demo - 0 + 管理数据的流程：state：存放管理的数据状态action：描述怎么修改数据reducer：（函数）根据 action 的表述生成一个新的 state3. 在 React 中使用 Redux（1）Redux Toolkit（RTK）RTK 是官方推荐的编写 Redux 逻辑的方式之一，是一套工具的集合，其作用如下：简化 store 配置内置 immer，支持可变式状态修改内置 thunk，便于异步的创建和使用（2）react-redux连接 Redux 和 React 组件的中间件（3）redux-demo包含：cli 脚手架 redux 计数器案例、提交 action 传参、异步状态操作https://github.com/suaxi/develop-relevant-notes/tree/main/front-end/React18/code/redux-demo