react入门(中)

135 阅读7分钟

最后更新时间 2025/04/20

自定义hook

    import { useState } from 'react'
    // 把可复用的逻辑写在一个自定义的 hook 中,最后返回设定的值和方法,可以以数组或是对象的形式返回
    const useToggle = () => {
      const [value, setValue] = useState(false)
      const toggle = () => setValue(!value)
      return [value, toggle]
    }

    const App = () => {
      // 使用自定义 hook
      // 调用 useToggle 返回的值和方法
      const [value, toggle] = useToggle()
      return (
        <div>
          <button onClick={toggle}>Toggle</button>
          <div>{value && 'Hello World'}</div>
        </div>
      )
    }

    export default App

状态管理

redux

可以理解为vue中的vuex和pinia,进行集中式状态管理

redux toolkit 官方推荐redux逻辑的方式,是一套工具集,简化书写方式

react-redux 用来链接react组件和redux的中间件

安装插件

    pnpm i @reduxjs/toolkit react-redux

定义 store

    // src/store/modules/user.jsx
    import { createSlice } from '@reduxjs/toolkit';

    // 创建一个包含初始状态、同步方法的 slice
    const userSlice = createSlice({
      name: 'user',
      initialState: { token: '00002' },
      reducers: {
        setToken: (state, action) => {
          state.token = action.payload;
        },
        delToken: (state) => {
          state.token = '';
        },
      },
    });

    // 解构出创建 action 对象的函数
    const { setToken, delToken } = userSlice.actions;

    // 获取 reducer 函数
    const userReducer = userSlice.reducer;

    // 导出 action 和 reducer
    export { setToken, delToken };
    export default userReducer;

中转配置 store

    // src/store/index.tsx
    import { configureStore } from '@reduxjs/toolkit';
    import userReducer from './modules/user';

    // 配置 Redux store
    // 根 store 组装所有子模块 
    const store = configureStore({
      reducer: {
        user: userReducer,
      },
    });

    export default store;

注入 store

    # main.jsx

    import React from 'react'
    import ReactDOM from 'react-dom/client'
    import {RouterProvider} from 'react-router-dom'
    import { Provider } from 'react-redux'
    import store from './store'
    import router from './router'

    ReactDOM.createRoot(document.getElementById('root')!).render(
      <React.StrictMode>
       <Provider store={store}>
        <RouterProvider router={router} />
       </Provider>
      </React.StrictMode>
    )

组件中使用store

    import { useSelector, useDispatch } from 'react-redux'
    import { setToken, delToken } from '../../store/modules/user'
    function App() {
      const { token } = useSelector((state: any) => state.user)
      const dispatch = useDispatch()
      return (
        <>
          <span>{token}</span>
          <br />
          <button onClick={() => dispatch(setToken('123'))}>设置token</button>
          &nbsp;
          <button onClick={() => dispatch(delToken())}>删除token</button>
        </>
      )
    }
    export default App

Zustand

安装

    pnpm i zustand

定义 store

    // 从 zustand 库导入 create 函数,用于创建新的 store 实例
    import { create } from 'zustand';

    // 使用 create 函数创建一个新的 store 实例
    const userStore = create((set) => {
      // 返回一个对象,作为 store 的初始状态和方法
      return {
        // store 的初始状态,token 被初始化为 '0002'
        token: '0002',
        
        // 定义一个方法 setToken,接收一个 payload 参数,并使用 set 函数更新 token 的值
        setToken: (payload) => set({ token: payload }),
        
        // 定义一个方法 delToken,不接收任何参数,调用后将 token 更新为空字符串 ''
        delToken: () => set({ token: '' })
      }
    })

    // 将 userStore 导出为默认导出,以便在其他模块中使用
    export default userStore;

组件中使用

    import userStore from '../../store/modules/user'
    function App() {
      const { token, setToken, delToken } = userStore()
      return (
        <>
          <span>{token}</span>
          <br />
          <button onClick={() => setToken('123456')}>设置token</button>
          &nbsp;
          <button onClick={() => delToken()}>删除token</button>
        </>
      )
    }
    export default App

Easy-peasy

建立在redux之上,看单词就能看出来,翻译过来就是非常简单,小菜一碟

直接上手,安装插件

    pnpm i easy-peasy

我们在根目录新建一个store文件夹,再新建一个index.tsx对所有的状态进行中转,再新建一个modules对各自的状态进行管理

    src/
    |-- store/
    |   |-- index.js
    |   |-- modules/
    |       |-- user.js
    |-- App.js

action 用于定义同步状态

  • action 回调 中的两个参数 state, payload
  • 第一个参数 state 是当前数据的状态,可以直接修改来更新状态
  • 第二个参数 payload 是传递给 action 的数据

thunk 用于定义异步状态

  • thunk 回调 中有两个参数 actions, payload
  • 第一个参数 actions 是当前模块的 action,可以调用 action 来更新状态
  • 第二个参数 payload 是调用异步函数传递给 thunk 的数据

定义

    # user.jsx

    import { action, thunk } from 'easy-peasy'
    import request from '@/utils/request'
    // 定义 user 模块
    const user = {
      token: '00002',
      setToken: action((state, payload) => {
        state.token = payload
      }),
      delToken: action(state => {
        state.token = ''
      }),
      getToken: thunk(async actions => {
        const { data } = await request.get('/get/token')
        actions.setToken(data)
      }),
    }

    export default user

中转

    # index.jsx

    import { createStore } from 'easy-peasy';
    import user from './modules/user'

    /**
    * 在 index.tsx 中导入createStore 方法创建一个 store 实例
    */

    // 组合所有模块
    const storeModel = {
      user:user
    }

    // 创建 store 实例存储
    const store=createStore(storeModel)

    export default store;

组件中使用

useStoreStateuseStoreActions 都接收一个回调函数。这种方式允许你访问和使用 store 的状态或 actions,而不是直接暴露整个 store 对象。这样可以避免不必要的重新渲染,确保组件仅在相关状态或 actions 发生变化时才更新

    import { useStoreState, useStoreActions } from 'easy-peasy' // 在要使用的组件中导入 useStoreState 和useStoreActions hook 来访问和操作 store 中的状态
    const home = () => {
      const token = useStoreState((state)=> state.user.token)
      const setToken = useStoreActions((state)=> state.user.setToken)
      const delToken = useStoreActions((state)=> state.user.delToken)
      return (
        <>
        <div>{token}</div>
          <button onClick={()=>setToken('123456')}>设置token</button>
          <br />
          <button onClick={()=>delToken()}>清除token</button>
        </>
      )
    }

    export default home
``
![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/04b188de19e444b6aceb4beba6e29563~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgZGxlZWk=:q75.awebp?rk3s=f64ab15b&x-expires=1748421236&x-signature=8D0MSaLCt190dOHeIIaBAsxR50g%3D)

## ReactRouter

安装 react-router-dom 包
```bash
    pnpm i react-router-dom

    import React from 'react'
    import ReactDOM from 'react-dom/client'
    import { RouterProvider } from 'react-router-dom' // 引入路由组件
    import  router  from './router' // 引入路由实例

    ReactDOM.createRoot(document.getElementById('root')!).render(
      <React.StrictMode>
        <RouterProvider router={router}></RouterProvider>   RouterProvider 用来提供路由出口并绑定路由
      </React.StrictMode>
    )

配置路由信息

    import { createBrowserRouter } from 'react-router-dom'

    import Home from '../pages/home'
    import Article from '../pages/article'

创建路由实例对象

    const router = createBrowserRouter([
      {
        path: '/',
        element: <Home />, // element 表示要渲染的组件 ,就是 vue 中的component
      },
      {
        path: '/article',
        element: <Article />,
      },
    ])
    // 导出路由实例对象
    export default router

路由跳转

编程式导航

使用 useNavigate 可以理解为 vue 中的 router.push()方法

    import { Link , useNavigate} from "react-router-dom"

    const Home = () => {
       const navigate = useNavigate()
      return (
        <div>
          <Link to={"/article"}>article</Link>
          <h1>home</h1>
          <button onClick={() => navigate('/article')}>去文章页面</button>
        </div>
      )
    }
    export default Home

声明式导航

使用 Link 标签 to 属性后为跳转的路径地址,在实际渲染时会被渲染为 html 的 a 标签

    import { Link } from "react-router-dom"

    const Home = () => {
      return (
        <div>
          <Link to={"/article"}>article</Link>
          <h1>home</h1>
        </div>
      )
    }
    export default Home

路由传参

get 路径地址传参

通过 useSearchParams 获取参数

    // 首页
    import { useNavigate, Link } from 'react-router-dom'

    const home = () => {
      const navigate = useNavigate()
      return (
        <div>
          <h1>Home</h1>
          <button onClick={() => navigate('/article?id=1001&name=react')}>去文章页</button>
        </div>
      )
    }

    export default home
    // 详情页
    import { Link } from 'react-router-dom'
    import { useSearchParams } from 'react-router-dom'
    // 使用了useSearchParams钩子来获取URL中的查询参数。useSearchParams返回一个数组,其中第一个元素是SearchParams对象,第二个元素是一个函数,用于更新查询参数
    const Article = () => {
      const [params] = useSearchParams()
      const id = params.get('id') // 通过get方法获取参数
      const name = params.get('name')
      return (
        <div>
          <Link to='/home'>回到首页</Link>
          <h1>article</h1>
          <p>id:{id}--name:{name}</p>
        </div>
      )
    }
    export default Article

post请求体传参

通过useParams获取请求参数

    // 首页
    import { useNavigate, Link } from 'react-router-dom'

    const home = () => {
      const navigate = useNavigate()
      return (
        <div>
          <h1>Home</h1>
          <button onClick={() => navigate('/article/1002/react')}>去文章页</button>
        </div>
      )
    }

    export default home

去到路由的位置,加上占位符

    const router = createBrowserRouter([
        { path: '/article/:id/:tile', element: <Article /> }
    ])

详情页

    import { Link,useParams } from 'react-router-dom'
    const Article = () => {
      const params = useParams()
      const id = params.id // params 是通过.语法来获取传递的参数
      const name = params.name
      return (
        <div>
          <Link to='/home'>回到首页</Link>
          <h1>article</h1>
          <p>id:{id}----name:{name}</p>
        </div>
      )
    }
    export default Article
    ```
    

### 路由配置文件
在Router6.x中,为我们提供了**useRoutes API**可以完成相关的配置


![image-20230426060313235-1683026306063-104.png](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/b0e85d5eb5f4411bb800fd91ed71f077~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgZGxlZWk=:q75.awebp?rk3s=f64ab15b&x-expires=1748421236&x-signature=pRV9RcjJBxAeTzRbJlZdnUZRp9A%3D)

使用

```jsx
import { useRoutes } from "react-router-dom"
import routes from '@/routes

<div>{ useRoutes(routes) }</div>

image-20230426060016775-1683026306063-107.png

嵌套路由

使用children表示对应的二级路由地址

    import { createBrowserRouter } from 'react-router-dom'

    import Home from '../pages/home'
    import Article from '../pages/article'
    // 创建路由实例对象
    const router = createBrowserRouter([
      {
        path: '/',
        element: <Home />, // element 表示要渲染的组件 ,就是 vue 中的component
        children: [
          {
            path: 'article',
            element: <Article />,
          }
        ]
      },

    ])
    // 导出路由实例对象
    export default router

在要渲染二级路由的地方,放置 Outlet 标签,和vue中的 view-router 是一个意思

        import { Link , Outlet} from "react-router-dom" // outlet 表示占位符,表示二级路由的渲染位置 (可以理解为 Vue 中的 RouterView)
        const Home = () => {
          return (
            <div>
              <Link to='/article'>article</Link>
              <h1>home</h1>
              <Outlet/>
            </div>
          )
        }
        export default Home

默认显示二级路由

去掉二级路由的 path 属性替换为 index:true


    import { createBrowserRouter } from 'react-router-dom'

    import Home from '../pages/home'
    import Article from '../pages/article'
    // 创建路由实例对象
    const router = createBrowserRouter([
      {
        path: '/',
        element: <Home />, // element 表示要渲染的组件 ,就是 vue 中的component
        children: [
          {
            index: true, // index 表示当前路由是默认路由
            element: <Article />,
          }
        ]
      },

    ])
    // 导出路由实例对象
    export default router

404 页面配置

    import { createBrowserRouter } from 'react-router-dom'

    import Home from '../pages/home'
    import Article from '../pages/article'
    // 创建路由实例对象
    const router = createBrowserRouter([
      {
        path: '/',
        element: <Home />, // element 表示要渲染的组件 ,就是 vue 中的component
        children: [
          {
            index: true, // index 表示当前路由是默认路由
            element: <Article />,
          }
        ]
      },
      {
        path: '*',
        element: <div>404</div> // 表示 404 页面
      }

    ])
    // 导出路由实例对象
    export default router

路由懒加载

  1. 使用 react 内置的lazy函数实现路由懒加载
  2. Suspense 组件包裹路由中element选项对应的组件

    import { createBrowserRouter } from 'react-router-dom'
    import { lazy, Suspense } from 'react'

    const Detail = lazy(() => import('../pages/detail/index'))
    const Home = lazy(() => import('../pages/home/index'))

    const router = createBrowserRouter([
      {
        path: '/',
        element: (
          <Suspense>
            <Home />
          </Suspense>
        ),
        children: [
          {
            path: 'detail',
            element: (
              <Suspense>
                <Detail />
              </Suspense>
            ),
          },
        ],
      }
    ])

    export default router

本文为个人学习记录,内容会根据学习进度不定期补充和更新,难免存在一定的疏漏或错误。若发现任何问题,恳请指正与建议