React学习三:Redux和ReactRouter

发布时间 2023-11-28 14:30:08作者: 数星观月

Redux

redux是一个仓库,用来存储数据。当react中多个组件需要用到同一个数据时,如果我们一次次地请求会比较麻烦,也会加大服务器的压力。这种多组件用到的数据就可以存放到redux仓库中。

redux仓库使用原生的依赖创建比较繁琐,为了更快创建使用"@reduxjs/toolkit"这个依赖包。创建好的仓库在react中还不能直接使用,必须需要引入"react-redux"来连接react项目和创建好的仓库。下面简单演示一下使用。

首先下载安装依赖

npm install @reduxjs/toolkit react-redux

依赖安装好之后就是创建仓库。仓库的设计思路就是在src目录下创建一个store文件夹,下面建一个index.js当做仓库,建一个modules文件夹里面存放仓库中存储的每个模块数据。

仓库架子搭好后就是从模块数据开始创建仓库。在modules下面创建一个模块数据的js文件,引入"@reduxjs/toolkit"的createSlice方法创建模块仓库,里面name是仓库名,initialState设置数据的初始值,reducers设置改变数据的方法(仓库里面的数据不能直接修改,必须调用reducers里面的方法才行)。使用仓库名.actions将reducers里面修改仓库数据的方法解构赋值出去,然后按需导出,方便其他组件使用钩子修改。使用仓库名.reducer将仓库中的数据解构出来,然后默认导出,后续放在index.js里面的总仓库中,方便管理。仓库里面还可以定义异步修改仓库数据方法,里面需要传入形参dispatch,这个形参非常巧妙后面使用异步方法时再介绍。下面是代码案例。

import { createSlice } from '@reduxjs/toolkit'
import { userInfo } from '@/apis/user'
const userStore = createSlice({
  name: 'user',
  initialState: {
    userInfo:{}
  },
  reducers: {
    setUserInfo(state,action) {
      state.userInfo = action.payload
    }
  }
})
const { setUserInfo} = userStore.actions
const fetchUserInfo = () => {
  return async (dispatch) => {
    const res = await userInfo()
    dispatch(setUserInfo(res.data))
  }
}
export { setUserInfo,fetchUserInfo}
const userReducer = userStore.reducer
export default userReducer

然后在index.js中注册这个模块,代码如下

import userReducer from './modules/user'
import { configureStore } from '@reduxjs/toolkit'
export default configureStore({
  reducer: {
    user:userReducer
  }
})

仓库创建好了,下面是使用。因为仓库在项目中不能直接使用,需要用到react-redux来连接仓库和项目。在项目的根目录的index.js入口文件中配置,引入react-redux中的Provider模块,引入总仓库,在回调中使用模块包裹住路由模块或其他模块。

import { Provider } from 'react-redux';
import store from './store';
root.render(
  <Provider store={store}>
    <RouterProvider router={router} />
  </Provider>
);

接着就可以在组件中使用仓库中的数据和使用结构出来的方法修改仓库数据。这里有一个需要注意的点,因为仓库和项目不是直连的,中间隔了一个react-redux,所以组件用仓库里面的数据和actions方法都要使用react-redux里面的钩子。下面演示一下勾取数据的钩子useSelector,勾取actions的钩子useDispatch。这两个都是hook函数,必须在外层使用。这里的state就是index里面的那个总仓库,这里拿出来的数据是响应式的,其他地方修改了这个数据(一般是异步),那么视图也会改变。仓库中的异步方法是非常有必要的,仓库数据存储的都是都是后端返回的数据,我们一旦修改了后端的值,那么我们仓库中的数据也是需要更新的。如果再发送请求,再调用同步actions就会比较麻烦。这时异步修改的好处就体现出来了,将请求和actions封装在一起,数据改变,调用异步方法直接更新。这里非常有意思的是异步方法也是需要用到dispatch钩子,异步方法里面还将dispatch钩子作为形参传入,因为同步actions中需要调用到dispatch钩子,这样就是说异步方法需要调用两次dispatch钩子。如果在useEffect中需要使用dispatch钩子需要在依赖项传入。以上就是redux的简单使用流程。

import { useDispatch, useSelector } from "react-redux"
const userInfo = useSelector(state => state.user.userInfo)
const dispatch = useDispatch()
const onFinish = () => {
  dispatch(fetchUserInfo())
  message.success("修改成功")
}

React-Router

react-router是路由,下面简单演示一下使用。

下载依赖包

npm install react-router-dom

在项目根目录src下面创建router文件夹,创建index.js文件,里面创建并设置路由。如果有二级路由的话还需要在return的html中配置二级路由的出口。

import Login from '@/pages/Login'
import ArticleLayout from '@/pages/Layout'
import { createBrowserRouter } from 'react-router-dom'
import { AuthRouter } from '@/components/AuthRouter'
import Home from '@/pages/Home'
const router = createBrowserRouter([
  {
    path: '/',
    element:<Login/>
  },
  {
    path: '/layout',
    element: <AuthRouter><ArticleLayout /></AuthRouter>,
    children: [
      {
        index: true,
        element:<Home/>
      }
    ]
  },
])
export default router
import { Outlet } from 'react-router-dom'
return (
  <Content className='contentStyle'>
    <Outlet />
  </Content>
)

路由创建好后需要在项目根目录的index.js入口文件中引入路由模块

import router from './router';
import { RouterProvider } from 'react-router-dom';
root.render(
  <Provider store={store}>
    <RouterProvider router={router} />
  </Provider>
);

路由使用的话就是路由跳转和获取路由参数,路由跳转可以使用钩子,也可以使用特定的路由组件,如下

import { Link, useNavigate } from 'react-router-dom'
const navigate = useNavigate()
return (
    <div>
      这是登录页
      {/* 声明式写法,适合用在html中 */}
      <Link to="/article">跳转到文章页</Link>
      <button onClick={() => navigate('/article')}>跳转到文章页</button>
      <button onClick={() => navigate('/article?id=100&&name=steven')}>searchParams传参</button>
      <button onClick={()=>navigate('/article/100/steven')}>params传参</button>
    </div>
  )

接受参数,如下。使用useParams来接受参数需要注意在路由项中配置"/:参数名"

import { useSearchParams, useParams } from "react-router-dom"
const Article = () => {
  // const [params] = useSearchParams()
  // const id = params.get('id')
  // const name = params.get('name')
  const params = useParams()
  const id = params.id
  const name = params.name
  return <div>这是文章页{id}-{name}</div>
}

配置路由拦截,就是在路由组件外面再加上一个拦截组件,做一些判断来决定是否返回路由组件还是跳转到别的路由。

import { getToken } from "@/utils"
import { Navigate } from "react-router-dom"
export function AuthRouter({children}) {
  const token = getToken()
  if (token) {
    return <>{ children}</>
  } else {
    return <Navigate to="/" replace/>
  }
}