redux全网最详细教程

一.路由懒加载

关键点:
lazy懒加载
Suspense组件(添加加载提示)

utils文件夹
–LazyLoad.js

//lazy懒加载 Suspense 组件(添加加载提示)
import {
    
    lazy,Suspense} from 'react'
export default function LazyLoad(url){
    
    
    //导入组件
    const Elem=lazy(()=>import('../pages'+url))
    //Suspense 提供一个加载提示
    return 
    <Suspense fallback={
    
    <h2>loading...</h2>}>
        <Elem></Elem>
    </Suspense>
}

router文件夹–
baseRoutes.js
导入lazyLoad
import LazyLoad from "../utils/LazyLoad";

import Home from "../pages/Home";
import About from "../pages/About";
import Produce from "../pages/Produce";
// import OrderList from "../pages/admin/OrderList";
import Admin from "../pages/admin/Admin";
// import Dash from "../pages/admin/Dash"
import LazyLoad from "../utils/LazyLoad";
// 基本路由配置
const baseRoutes = [
    {
    
    
      path: "",
      element: <Home></Home>,
    },
    {
    
    
      path: "/about",
      element: <About></About>,
    },
    {
    
    
        path: "/produce/:id",
        element: <Produce></Produce>,
      },
      {
    
    
        path:"/admin/*",
        element:<Admin></Admin>,
        children:[
          {
    
    path:'',element:LazyLoad('/admin/Dash.js')},
          {
    
    path:'dash',element:LazyLoad('/admin/Dash.js')},
          {
    
    path:'orderlist',element:LazyLoad('/admin/OrderList.js')},
        ]
      }
  ];
  export default baseRoutes

index.js

import {
    
     useEffect, useState } from "react";
// useRoutes 根据路由配置 创建路由
import {
    
     useRoutes } from "react-router-dom";
import baseRoutes from "./baseRoutes";
function RouterView() {
    
     
  // 常见路由
  const element = useRoutes(baseRoutes);
  return <>{
    
    element}</>;
}
export default RouterView;

lazy Suspense
此时我们发现第一次点击的时候会出现路由懒加载,第二次就不再请求数据

react实现登录

npm i axios

  1. 定义request.js
    utils —request.js
import axios from 'axios'
const request=axios.create({
    
    
    baseURL:"http://dida100.com:8888",
    timeout:5000


})
request.interceptors.request.use(config=>{
    
    
    if(sessionStorage.getItem("token")){
    
    
        config.headers["Authorization"]="Bearer "+sessionStorage.setItem("token")
    }
    return config
})
export default request
  1. 定义api
    新建server文件夹
    user.js async 和 await 结合 数据请求完才往下走
import request from '../utils/request'
export async function loginServer(data){
    
    
    //async 和 await 结合
    const result=await request.post("/api/login",data)
    //状态码为200 成功
    if(result.status===200){
    
    
        return result.data
    }else{
    
    
        //请求失败
        //throw抛出 新的错误
        throw new Error("网络请求失败")
    }
}
  1. 实现登录
    Home.js
    (没有载荷是由于接口里面没写data) 这里面input实现双向绑定是通过onChange事件 onChange={e=>setUser({...user,password:e.target.value})}这里的...是扩展,就是面的name与password,用新的值去替换前面的password
import {
    
    loginServer} from '../server/user'
import {
    
    useState} from 'react'
import {
    
    useNavigate,useLocation} from 'react-router-dom'
import {
    
    parse} from '../utils'
function Home() {
    
    
    const [user,setUser]=useState({
    
    name:"",password:""})
    const navigate=useNavigate()
    const location=useLocation()
    var search=parse(location.search)
    function login(){
    
    
        loginServer(user)
        .then(res=>{
    
    
            if(res.code===200){
    
    
                sessionStorage.setItem("token",res.token)
                sessionStorage.setItem("user",JSON.stringify(res.user))
                var redirect=search.redirect||'admin'
                navigate(redirect)
            }
        })
    }
    return ( <div>
        用户名:<input value={
    
    user.name} onChange={
    
    e=>setUser({
    
    ...user,name:e.target.value})}/><br />
        密码:<input value={
    
    user.password} onChange={
    
    e=>setUser({
    
    ...user,password:e.target.value})}/><br />
        <button onClick={
    
    login}>登录</button>
    </div> );
}


export default Home;

权限管理

通过新建一个组件实现的 pages页面新建一个private.js 如果有token就跳转到private的子组件props.children,如果没有就返回首页

import {
    
    Navigate,useLocation} from 'react-router-dom'
function Private(props) {
    
    
    const location=useLocation()
    console.log(location);
    if(sessionStorage.getItem("token")){
    
    
        return <>{
    
    props.children}</>
    }else{
    
    
        return <Navigate to={
    
    {
    
    pathname:'/',search:"?redirect="+location.pathname}} />;
    }
    
}


export default Private;

在路由基础配置中的element中,用<Private><Admin></Admin></Private进行包裹子组件Admin

import Private from "../pages/Private";
const baseRoutes = [
path:"/admin/*",
element:<Private><Admin></Admin></Private>,
  ]

Home.js

import {
    
    useNavigate,useLocation} from 'react-router-dom'

import {
    
    parse} from '../utils'
 const location=useLocation()
//把location的search转换为对象
 var search=parse(location.search)
如果search对象有redirext属性就用redirect,没有返回admin
var redirect=search.redirect||'admin'
utils里面 index.js
// 把?name=mumu&age=18
// 转换为{name:"mumu",age:18}
export function parse(url) {
    
    
    var temp = url.slice(1).split("&");
    var search = {
    
    };
    for (var i = 0; i < temp.length; i++) {
    
    
      var item = temp[i].split("=");
      search[item[0]] = item[1];
    }
    return search;
  }

redux(老版本)

单向数据流

组成
state状态数据
reducer处理状态的方法
store仓库
actions调用reducer的方法

安装
npm i redux react-redux redux-thunk redux-logger @reduxjs/toolkit -S
redux全局状态管理
react-redux 连接组件与redux
redux-thunk 处理异步actions
redux-logger 日志
redux-persist 本地存储

新建store
—index.js整个仓库

//整个仓库
// createStore 创建仓库,applyMiddleware处理中间件(日志,异步,本地存储)
// combineReducers合并多个处理counterReducer,userReducer,productionReducer
import {
    
    createStore,applyMiddleware,combineReducers} from 'redux'
//导入处理计数器
import counterReducer from './counterReducer'
//导入异步处理
import thunk from 'redux-thunk'
//导入日志
import logger from 'redux-logger'
//创建仓库
const store=createStore(
    combineReducers({
    
    counter:counterReducer}),
    applyMiddleware(thunk,logger)
)
//导出仓库
export default store

store有三个核心方法:
dispatch发起动作 触发reducer 返回新的state
subscribe 监听store中state的变化 执行回调函数
getState 获取stoTe中的数据

counterReducer.js 处理counter数据

import {
    
    INCREMENT} from './types'
//处理counter数据 定义初始化state
const initialState={
    
    
    num:1
}
//定义一个处理器

function counterReducer(state=initialState,action){
    
    
    //根据动作的类型
    switch(action.type){
    
    
        case INCREMENT:
            //如果是加,返回原来的数据state,把num值加1
            return {
    
    ...state,num:state.num+1}
        default:
            return state
    }
}
export default counterReducer


counterAction.js调用CounterReducer动作
type后面跟的是常量,需要两个页面一致,所以我们
store --type.js 存放reducer动作类型
export const INCREMENT=“INCREMENT”
在其他页面导入,引号就可以去掉了,不容易出错,很规范
import {INCREMENT} from './types'

store有三个核心方法(原生):
dispatch发起动作 触发reducer 返回新的state
subscribe 监听store中state的变化 执行回调函数

getState 获取store中的数据

第一种方法

About.js
原生的三个都得写
这三个方法哪来的?
我们在index.js创建仓库的时候就带了这三个方法,通过createStore方法获取的
counter怎么来的?
const store=createStore( combineReducers({counter:counterReducer}), applyMiddleware(thunk,logger) )

dispatch(itype: “INCREMENT”})中的参数就是counterReducer的action根据type的值

//导入仓库 getState获取state 
//dispatch发送事件执行 reducer改变state subscribe监听state数据的变化
import store from '../store/index.js'
//导入动作
import {
    
    add} from '../store/counterAction'
import {
    
    useEffect,useState} from 'react'
function About() {
    
                 
    //创建value的状态 响应式更新store中的num 初始赋值
	
    const [value,setValue]=useState(store.getState().counter.num)
    useEffect(()=>{
    
    
        //监听store变化用store的num更新组件的value
        store.subscribe(()=>{
    
    
            setValue(store.getState().counter.num)
        })
      
    },[])
    return ( 
      
        <div>about
            <p>value-store里面的num:{
    
    value}</p>
            <button onClick={
    
    ()=>store.dispatch(add())}>+</button>
        </div>
     );
}


export default About;

第二种方式 react-redux use方法

index.js
导入数据提供器Provider
import {Provider} from 'react-redux'
包装
<Provider store={store}> <App /> </Provider>
使用

const num=useSelector(state=>state.counter.num)
 const dispatch=useDispatch()
onClick={
    
    ()=>dispatch({
    
    type:"INCREMENT"})}
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';


//导入store
import store from './store'
//导入数据提供器
import {
    
    Provider} from 'react-redux'


const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  // 渲染多次 去掉严格模式
  // <React.StrictMode>
  <Provider store={
    
    store}>
    <App />
  </Provider>
    
  // </React.StrictMode>
);

OrderList使用选择器useSelector
<button onClick={()=>dispatch({type:"INCREMENT"})}>{num}</button> <button onClick={()=>dispatch(add())}>{num}</button>
这俩方式都可以实现

//使用选择器
import {
    
    useSelector,useDispatch} from 'react-redux'
//导入动作
import {
    
    add} from '../../store/counterAction'
function OrderList() {
    
    
    //选择到state中的counter的num
    const num=useSelector(state=>state.counter.num)
    //创建一个触发器
    const dispatch=useDispatch()
    return (  
        <div>
            orderlist
            <p>
                {
    
    /* 单击发送动作给reducer 处理返回新的state数据 */}
                <button onClick={
    
    ()=>dispatch({
    
    type:"INCREMENT"})}>{
    
    num}</button>
                <button onClick={
    
    ()=>dispatch(add())}>{
    
    num}</button>
            </p>
        </div>
    );
}


export default OrderList;

他们之前是同步的,其他页面也会改变

第三种方式

produce.js

import {
    
    useParams} from 'react-router-dom'
//导入连接器 把state和actions方法转换props
import {
    
    connect} from 'react-redux'
//导入counterAction 里面的所有动作方法
import * as actions from '../store/counterAction'
console.log(actions);
function Produce(props) {
    
    
    //获取产品参数
    const params=useParams()
    return ( 
        <div>产品页面-{
    
    params.id}
        {
    
    /* <p onClick={()=>props.add()}>{props.counter.num}</p> */}
        <p onClick={
    
    ()=>props.add()}>{
    
    props.num}</p>
        </div>
     );
}
//props.counter.num从connect第一个参数 状态
//props.add 从connect的第二个参数  方法
// export default Produce;
export default connect(state=>({
    
    num:state.counter.num}),{
    
    add:actions.add})(Produce)


猜你喜欢

转载自blog.csdn.net/m0_55734030/article/details/129051307