[react]react-router-dom 与 redux 版本升级

[react]react-router-dom 与 redux 版本升级

  • 环境
  • 脚手架的升级
  • [react-router-dom 升级](#react-router-dom 升级)
  • 状态管理
    • [React 状态管理--Redux4.0.4版本](#React 状态管理--Redux4.0.4版本)
    • [React 状态管理--Redux5.0.0版本](#React 状态管理--Redux5.0.0版本)
      • [store.js 定义所有全局状态](#store.js 定义所有全局状态)
      • [store.js 定义所有全局状态--持久化版本](#store.js 定义所有全局状态--持久化版本)
      • 组件使用全局变量:
      • 报错信息
        • [store.js:60 A non-serializable value was detected in an action, in the path: `register`. Value: ƒ register2(key) { _pStore.dispatch({ type: REGISTER,key});}](#store.js:60 A non-serializable value was detected in an action, in the path: register. Value: ƒ register2(key) { _pStore.dispatch({ type: REGISTER,key});})
  • 生命周期
  • 问题汇总
    • [Module not found: Can't resolve 'web-vitals'](#Module not found: Can't resolve 'web-vitals')
    • [Cannot access 'WEBPACK_DEFAULT_EXPORT' before initialization](#Cannot access 'WEBPACK_DEFAULT_EXPORT' before initialization)
    • [State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect()](#State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect())
    • [Line 296:7: React Hook "useEffect" is called in function "next" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"](#Line 296:7: React Hook "useEffect" is called in function "next" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use")

本文主要是数据状态管理的升级以及路由升级的相关设置

环境

  • node -v.18.15.0
  • react - 16.9.0

脚手架的升级

升级过程中遇到的各种各样的问题记录一下

官方提供了一部分[升级指南]--如何升级到 React 18,所以仅记录一些官网没有的问题

包之间是存在依赖性的,因此并不是同时升级所有的包是最优选择,首先选定必须要升级的包,先升级之后在运行查看

关于脚手架的升级可查看react脚手架的升级,我们最好是先升级脚手架

react-router-dom 升级

根据查看包的更新命令发现react-router-dom也已经由5.0.1升级到6.20.1版本了

发现之前的路由的写法已经不支持了

而官网上关于路由的介绍感觉有点混乱。。。

查了很多资料,最后整理一下

发现针对路由的配置简化了很多

关于路由相关文件的写法--react-router-dom 5.0.1

入口渲染文件App.js

Root 是所有路由的外框架,老版本是在./view/root/index中初始化所有的路由,新版本的可以直接配置在路由文件src/views/page.js中了

js 复制代码
import React from 'react';
import Root from './view/root/index';
import {Router, Switch, Route} from "react-router-dom";
import {Provider} from 'react-redux';
import {store} from './reducer/store';
import {createBrowserHistory} from "history";

const history = createBrowserHistory();

class App extends React.Component {
    render(){
        return (
            <Provider store={store}>
                <Router history={history}>
                    <Switch>
                        <Route component={Root}/>
                    </Switch>
                </Router>
            </Provider>
        );
    }
}
export default App;

路由框架src/views/root/index.js

js 复制代码
//老版
import React from 'react';
import {Switch, Route, Redirect} from "react-router-dom";
import {routerMap} from "../../view/pages";
import RouterGuide from '../routerguide/index'
const pagesRoute = () => {
    return routerMap.map((item, index) => {
      if("/"==item.path){
          return <Route path="/" exact key={index} render={(props) => {
                 return <Redirect to={{pathname: item.redirectPath, search: `${props.location.search}`}}/>
          }}/>
      }else{
        return <RouterGuide key={index} path={item.path} component={item.component} auth={item.auth}/>
      }      
    })
}

class Root extends React.Component {
    render() {
        return (
                <div className="main-content">
                    <Switch>
                        {pagesRoute()}
                        <Redirect to="/"/>
                    </Switch>
                </div>
        )
    }
}
export default Root;

路由守卫 src/views/routerguide/index.jsx

jsx 复制代码
import React from 'react';
import {Route} from "react-router-dom";
import {connect} from 'react-redux';
import {userMap} from '../../reducer/connect';
import {device} from 'device.js/dist/device'

class RouterGuide extends React.Component {

    componentWillMount(){
        let {path, auth, userId} = this.props
        if (window.WEIXIN && path !== "/mobile") {
            window.location.href = "/mobile";
        } else if (!device.mobile && path !== "/mobile") {
            window.location.href = "/mobile";
        } else if (device.mobile && !window.WEIXIN && ('' === userId.userId || null === userId.userId)) {
            if (auth) {
                window.location.href = "/index";
            }
        }
    }

    render(){
        let {path, component} = this.props
        return (
            <Route path={path} component={component}/>
        )
    }
}

export default connect(userMap.mapStateToProps, userMap.mapDispatchToProps)(RouterGuide);

路由文件src/views/page.js

js 复制代码
//老版
import IndexPage from "../view/index/index";
import UserPage from "../view/user/index";
import DetailPage from "../view/detail/index";
import ResultPage from "../view/result/index";
export const routerMap=[
    {
        path:'/',
        redirectPath:'/index',
        errorPath:'/mobile',
        redirect:true,
        auth:false
    },{
        path:'/index',
        component:IndexPage,
        auth:false
    },
    {
        path:'/detail',
        component:DetailPage,
        auth:true
    },
    {
        path:'/user',
        component:UserPage,
        auth:true
    },
    {
        path:'/result',
        component:ResultPage,
        auth:false
    }
]

关于路由相关文件的写法--react-router-dom 6.20.1方式1

入口渲染文件App.js

js 复制代码
import React from "react";
import { createBrowserRouter } from "react-router-dom"; 
import Root from './view/root/index';
function App() {
  return (
    <BrowserRouter>
      <Root></Root>
    </BrowserRouter>
  );
}
export default App;

路由框架src/views/root/index.js

js 复制代码
import React,{Suspense} from "react";
import { Routes,Route,  Outlet } from "react-router-dom"; 
import {routerMap} from "../../view/pages";
import RouterGuide from '../routerguide/index'

// React.LazyExoticComponent<ComponentType<any>>
const withGuard = (item) => {
  return (
    <RouterGuide
      element={
        <Suspense>
          <item.element />
        </Suspense>
      }
      auth={item.auth}
    />
  );
};
const pagesRoute = () => {
  return routerMap.map((item, index) => {
    return (
      <Route
        path={item.path}
        key={index}
        element={withGuard(item)}
      />
    );
  });
};
function Root() {
  return (
      <div className="main-content">
        <Routes>
          {pagesRoute()}
        </Routes>
      </div>
  );
}
export default Root;

报错信息

[RouterGuide] is not a component. All component children of must be a or <React.Fragment>

该问题是想要像5.0.1版本一样,在pagesRoute设置路由守卫的时候报错了,<Routes></Routes>的内部只能是<Route/>,哪怕是定义路由守卫都不可以

直接在element设置为函数也报错,会报错Functions are not valid as a React child,但是调用函数,并使用Suspense标签即可

路由守卫 src/views/routerguide/index.jsx

新版的路由守卫可以直接在定义路由文件的时候直接设置,也即在方式2中通过src/views/page.js文件中设置,也可以单独设置

jsx 复制代码
import React, { useEffect, Suspense } from "react";
import { Route, useNavigate, useLocation, Navigate } from "react-router-dom";
import { store } from "../../reducer/store";
// 具体实现根据项目需求来进行处理,返回是路由地址。
const onRouterBefore = (one, auth) => {
  if (!device.mobile) {
    return "/mobile";
  } else if (auth) {
    const state = store.getState();
    return state.userId ? one.pathname : "/index";
  } else {
    return one.pathname;
  }
};
function RouterGuide({ element, auth }) {
  const location = useLocation();
  const navigate = useNavigate();
  const { pathname } = location;

  useEffect(() => {
    // onRouterBefore 是对路由地址进行处理的函数
    const nextPath = onRouterBefore(location, auth);
    if (nextPath && nextPath !== pathname) {
      //路由重定向
      navigate(nextPath, { replace: true });
    }
  }, [pathname]);
  return element;
}
export default RouterGuide;

路由文件src/views/page.js

路由守卫在定义时直接设置,并且相当于在配置文件中直接设定了Root是外框架,不像老版本是在Root编写的,写法更加方便简洁

js 复制代码
import Root from "../view/root/index"
import IndexPage from "../view/index/index";
import UserPage from "../view/user/index";
import DetailPage from "../view/detail/index";
import ResultPage from "../view/result/index";

const routerMap=[
    {
        path:'/',
        element:(IndexPage),
        errorElement: (ErrorPage),
        auth:false,
    },
    {
        path:'/index',
        element:(IndexPage),
        auth:false
    },
    {
        path:'/detail',
        element:(DetailPage),
        auth:true
    },
    {
        path:'/user',
        element:(UserPage),
        auth:true
    },
    {
        path:'/result',
        element:(ResultPage),
        auth:false
    }
]
export {routerMap}

关于路由相关文件的写法--react-router-dom 6.20.1方式2

入口渲染文件App.js

js 复制代码
//App.js 升级版
import React from "react";
import { RouterProvider,createBrowserRouter } from "react-router-dom"; 
import { routerMap } from "./view/pages";

const router = createBrowserRouter(routerMap);
function App() {
  return <RouterProvider router={router} />;
}
export default App;

路由框架src/views/root/index.js

js 复制代码
import React from "react";
import { Route,  Outlet } from "react-router-dom"; 
function Root() {
  return (
      <div className="main-content">
        <Outlet />
      </div>
  );
}
export default Root;

路由文件 src/views/page.js

路由守卫在定义路由时直接设置,并且相当于在配置文件中直接设定了Root是外框架,不像老版本是手动引入Root,再在Root中引入路由拦截编写的,该方式写法更加方便简洁

js 复制代码
import Root from "../view/root/index"
import IndexPage from "../view/index/index";
import UserPage from "../view/user/index";
import DetailPage from "../view/detail/index";
import ResultPage from "../view/result/index";

// 具体实现根据项目需求来进行处理,返回是路由地址。
const onRouterBefore = (one, auth) => {
  if (!device.mobile) {
    return "/mobile";
  } else if (one.pathname == "/") {
    return "/index";
  } else if (auth) {
    const state = store.getState();
    return state.userId ? one.pathname : "/index";
  } else {
    return one.pathname;
  }
};
function Guard({ element, auth }) {
  const location = useLocation();
  const navigate = useNavigate();
  const { pathname } = location;

  useEffect(() => {
    // onRouterBefore 是对路由地址进行处理的函数
    const nextPath = onRouterBefore(location, auth);
    if (nextPath && nextPath !== pathname) {
      //路由重定向
      navigate(nextPath, { replace: true });
    }
  }, [pathname]);
  return element;
}
// React.LazyExoticComponent<ComponentType<any>>
const withGuard = (Comp, auth) => {
  return (
    <Guard
      element={
        <Suspense>
          <Comp />
        </Suspense>
      }
      auth={auth}
    />
  );
};
const routerMap=[
    {
        path:'/',
        element:(Root),
        errorElement: (ErrorPage),
        auth:false,
        children:[
          {
              path:'/index',
              element:(IndexPage),
              auth:false
          },
          {
              path:'/detail',
              element:(DetailPage),
              auth:true
          },
          {
              path:'/user',
              element:(UserPage),
              auth:true
          },
          {
              path:'/result',
              element:(ResultPage),
              auth:false
          }
        ]
    }
]
const pagesRoute = (list)=>{
  list.map(item=>{
  item.element=withGuard(item.element,item.auth)
  if(item.children){
    pagesRoute(item.children)
  }
  })
}
pagesRoute(routerMap)
export {routerMap}

路由的跳转

代码跳转

使用老版的history路由的化话,如上面代码介绍 使用 createBrowserHistory,实现跳转的方法:

ts 复制代码
let {history} = this.props;
history.push('/detail')

新版本跳转方法:

ts 复制代码
import { useNavigate } from "react-router-dom";
const navigate = useNavigate();
navigate("/detail")

注意,不是 usenavigation,而是useNavigate跳转

标签跳转

js 复制代码
   <Route path="/" element={<Navigate to="/index" replace />}/>

状态管理

React 状态管理--Redux4.0.4版本

  • "redux": "^4.0.4"
  • "react-redux": "^7.1.0"

action.js定义改变状态类型

ts 复制代码
const USERID = 'userId';
const USERNAME = 'userName';
const PHONE = 'phone';
const ADDRESS = 'address';
const action_userId = {type: USERID, text: 'update the user id'}
const action_userName = {type: USERNAME, text: 'update user name'}
const action_phone = {type: PHONE, text: 'update the phone'}
const action_address = {type: ADDRESS, text: 'update address'}
export {USERID,USERNAME,PHONE,ADDRESS,action_userId,action_userName,action_phone,action_address}

connect.js定义组件需要修改的全局变量

ts 复制代码
import {
    action_userId, action_userName,action_phone,action_address
} from './action.js'

export const userMap = {
    mapStateToProps(state) {
        return state;
    },
    mapDispatchToProps(dispatch) {
        return {
            getUserId: () => {
                dispatch(action_userId)
            },
            getUserName: () => {
                dispatch(action_userName)
            }
        }
    }
};
//对使用到的状态更新
export const userInfoMap = {
    mapStateToProps(state) {
        return {
            userId: state.userId,
            phone: state.phone,
            address:state.address
        }
    },
    mapDispatchToProps(dispatch) {
        return {
            getUserId: () => {
                dispatch(action_userId)
            },
            getPhone: () => {
                dispatch(action_phone)
            },
            getAddress: () => {
                dispatch(action_address)
            }
        }
    }
};

redux.js 定义改变状态类型

ts 复制代码
import {combineReducers} from 'redux';
import {USERID,USERNAME,PHONE,ADDRESS} from './action.js'

/**
 * 触发的处理函数
 * @param state  设置一个初始值,若没有显示该初始值
 * @param action  
 * @returns {({} & {userId: string})|{userId: string}}
 */
const getUserId = (state = {userId: ''}, action) => {
    switch (action.type) {
        case USERID:
            return Object.assign({}, state, {
                userId: state.userId
            })
        default:
            return state
    }
}
/**
 * 身份证号
 * @param state
 * @param action
 * @returns {{userName: string}|({} & {userName: string})}
 */
const getUserName = (state = {userName: ''}, action) => {
    switch (action.type) {
        case USERNAME:
            return Object.assign({}, state, {
                userName: state.userName
            })
        default:
            return state
    }
}
/**
 *
 * @param state
 * @param action
 * @returns {({} & {phone: string} & {phone: *})|{phone: string}}
 */
const getPhone = (state = {phone: ''}, action) => {
    switch (action.type) {
        case PHONELABEL:
            return Object.assign({}, state, {
                phoneLabel: state.phone
            })
        default:
            return state
    }
}

/**
 *
 * @param state
 * @param action
 * @returns {({} & {address: string} & {address: *})}
 */
const getAddress = (state = {address: ''}, action) => {
    switch (action.type) {
        case ADDRESS:
            return Object.assign({}, state, {
                address: state.address
            })
        default:
            return state
    }
}
/**
 * 多个reducer方法 
 * @type {Reducer<any>}
 */
export const allReducer = combineReducers({
    userId: getPersonId,
    userName: getUserName,
    address: getAddress,
    phone:getPhone,
})

store.js 定义所有的全局变量

ts 复制代码
import {createStore} from 'redux'
import {allReducer} from './redux'
export const store = createStore(allReducer)

App.js 全局变量的注册:

ts 复制代码
import React from 'react';
import {Router, Switch, Route} from "react-router-dom";
import {Provider} from 'react-redux';
import {store} from './reducer/store';

import {createBrowserHistory} from "history";
import Template from './components/template/template';

const history = createBrowserHistory();
class App extends React.Component {
    render(){
        return (
            <Provider store={store}>
                <Router history={history}>
                    <Switch>
                        <Route component={Template}/>
                    </Switch>
                </Router>
            </Provider>

        );
    }
}
export default App;

组件使用全局变量

jsx 复制代码
import React from 'react'
import {connect} from "react-redux";
import {userInfoMap} from "../../reducer/connect";
import {setTitle} from '../../global';
import axios from 'axios';
import './index.css'

class index extends React.Component {
    constructor(props){
        super(props);
        this.next = this.next.bind(this);
        this.changeUserName = this.changeUserName.bind(this);
        this.changeAddress = this.changeAddress.bind(this);
        this.changePhone = this.changePhone.bind(this);
        this.clearValue = this.clearValue.bind(this);

        this.state = {
            userId:"",
            userName: '',
            phone: '',
            address: '',
            errorMes: ''
        }
    }
    clearValue(){
        this.setState({
            userName: '',
            phone: '',
            address: '',
            errorMes: ''
        })
    }
    changeUserName(event){
        let val = event.target.value;
        this.setState({
            userName: val,
        }, () => {
            if (val.length>10) {
                this.setState({
                    errorMes: "最大输入10个字符"
                })
            }
        })
    }
    changeAddress(event){
            let val = event.target.value;
            this.setState({
                address: val,
            }, () => {
                if (val.length>10) {
                    this.setState({
                        errorMes: "最大输入10个字符"
                    })
                }
            })
        }
    changePhone(event){
        let val = event.target.value;
        this.setState({
            phone: val,
        }, () => {
            if (val.length>11) {
                this.setState({
                    errorMes: "最大输入11个字符"
                })
            }
        })
    }
    login(){
        let {userId, address, phone,userName} = this.props;
        axios({
            method:'post',
            url:"/login", 
            data: {
                userName: this.state.userName,
                address: this.state.address,
                phone: this.state.phone
            }
        })
        .then((response) => {
            if (response.responseCode === "200") {
                //更新当前组件,局部状态变更
                this.setState({
                    userId: response.data.userId
                }, () => {
                    let {history} = this.props;
                    history.push('/home')
                })
                //更新全局数据
                userId.userId = response.data.userId;
                address.address = response.data.address;
                phone.phone = response.data.phone;
                userName.userName = response.data.userName;

            } else {
                //局部状态变更
                this.setState({
                    errorMes: '请求超时请稍后再试',
                }, () => {
                  //状态变更后的回调函数
                  // this.changeInput();
                })
            }
        })
    }

    next(){
      this.login();
    }
    //初次挂载完成
    componentDidMount(){
        setTitle("客户信息");
    }
    //组件卸载
    componentWillUnmount(){
        del();
    }
    render(){
        return (
            <div className="page">
              <div className="margin">
                  <div className="form-group top10">
                    <input type="text" value={this.state.userName}
                     onChange={(e) => this.changeUserName(e)} placeholder="请输入您的姓名"/>
                    <i onClick={() => this.clearValue()}></i>
                  </div>
                  <div className="form-group top10">
                    <input type="text" value={this.state.address}
                     onChange={(e) => this.changeAddress(e)} placeholder="请输入您的地址"/>
                    <i onClick={() => this.clearValue()}></i>
                  </div>
                  <div className="form-group top10">
                    <input type="text" value={this.state.phone}
                     onChange={(e) => this.changePhone(e)} placeholder="请输入您的手机号"/>
                    <i onClick={() => this.clearValue()}></i>
                  </div>

                  <div className="form-group">
                    <label onClick={() => this.next()}>下一步</label>
                  </div>
              </div>
            </div>
        )
    }
}

export default connect(userInfoMap.mapStateToProps, userInfoMap.mapDispatchToProps)(index)

React 状态管理--Redux5.0.0版本

  • "redux": "^5.0.0"
  • "@reduxjs/toolkit": "^2.0.1"
  • "react": "^18.2.0"

store.js 定义所有全局状态

ts 复制代码
import { createSlice, configureStore } from "@reduxjs/toolkit";

const initialState = {
  userId: "",
  userName: "",
  phone: "",
  address: "",
};
const statusSlice = createSlice({
  name: "stateGlobal",
  initialState: initialState,
  reducers: {
    appReducer: (state, action) => {
      return Object.assign({}, state, {
        ...action.payload
      });
    }
  }
});
export const store = configureStore({
  reducer: statusSlice.reducer
});
export const { appReducer } = statusSlice.actions;

store.js 定义所有全局状态--持久化版本

首先,需要添加持久化的包

js 复制代码
npm i redux-persist

其次,修改相关代码

ts 复制代码
import { createSlice, configureStore } from "@reduxjs/toolkit";
import { persistStore, persistReducer, PERSIST} from "redux-persist";
import storage from "redux-persist/lib/storage";

const initialState = {
  userId: "",
  userName: "",
  phone: "",
  address: "",
};
const statusSlice = createSlice({
  name: "stateGlobal",
  initialState: initialState,
  // devTools : 开启redux-devtools,默认开启,开发环境开启,生产环境关闭
  devTools: process.env.NODE_ENV === "development",
  reducers: {
    appReducer: (state, action) => {
      return Object.assign({}, state, {
        ...action.payload
      });
    },
    otherReducer:(state, action) => {
      return Object.assign({}, state, {
        userId:action.payload
      });
    },
  }
});
//在localStorge中生成key为root的值
const persistConfig = {
  key: "root",
  version: 1,
  storage,
  blacklist: [] //设置某个reducer数据不持久化,
};
const reducers = persistReducer(persistConfig, statusSlice.reducer);
const store = configureStore({
  reducer: reducers,
    middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {//设置序列化检测
        ignoredActions: [PERSIST],//忽略的action类型
        ignoredActionPaths: [],//忽略action中的路径
        ignoredPaths: []//忽略state中的路径
      }
      // serializableCheck: false //关闭redux序列化检测
    })
});
const persistor = persistStore(store);
export const { appReducer } = statusSlice.actions;
export { store, persistor };

组件使用全局变量:

jsx 复制代码
import React from 'react'
import { useNavigate } from "react-router-dom";
import { store, appReducer } from "../../reducer/store";
import {setTitle} from '../../global';
import axios from 'axios';
import './index.css'

export default function Index() {
  const navigate = useNavigate();
  //获取全局数据
  const init=store.getState(); 
  const [state,setState] = useState({
      userId:"",
      userName: '',
      phone: '',
      address: '',
      errorMes: ''
  })

  const clearValue=()=>{
      setState({
          ...state,
          userName: '',
          phone: '',
          address: '',
          errorMes: ''
      })
  }

  const changeUserName=(event)=>{
    let val = event.target.value;
    setState({
      ...state,
      userName: val,
      errorMes: val.length>10?"最大输入10个字符":state.errorMes
    })
  }
  const changeAddress=(event)=>{
    let val = event.target.value;
    setState({
      ...state,
      address: val,
      errorMes: val.length>10?"最大输入10个字符":state.errorMes
    })
  }
  const changePhone=(event)=>{
      let val = event.target.value;
      setState({
      ...state,
      phone: val,
      errorMes: val.length>11?"最大输入11个字符":state.errorMes
    })
  }

  const login=()=>{                 
    axios({
        method:'post',
        url:"/login", 
        data: {
            userName: state.userName,
            address: state.address,
            phone: state.phone
        }
    })
      .then((response) => {
          if (response.responseCode === "200") {
              //更新当前组件,局部状态变更
              setState({
                ...state,
                userId: response.data.userId
              })
              navigate('/home')
              //更新全局数据
              store.dispatch(appReducer({
                userId: response.data.userId,
                address: response.data.address,
                phone: response.data.phone,
                userName: response.data.userName,
              }))

          } else {
              //局部状态变更
              setState({
                ...state,
                errorMes: "请求超时请稍后再试"
              })
              //状态变更后的回调函数,没法设置,setState不支持回调
              // changeInput();
          }
      })
  }

  const next=()=>{
    login();
  }
  //初次挂载完成
  useEffect(()=>{
      setTitle("客户信息");
  },[])
  //组件卸载
  useEffect(()=>{
      return ()=>{
        del();
      }
  })
  
  return (
      <div className="page">
        <div className="margin">
            <div className="form-group top10">
              <input type="text" value={state.userName}
                onChange={(e) => changeUserName(e)} placeholder="请输入您的姓名"/>
              <i onClick={() => clearValue()}></i>
            </div>
            <div className="form-group top10">
              <input type="text" value={state.address}
                onChange={(e) => changeAddress(e)} placeholder="请输入您的地址"/>
              <i onClick={() => clearValue()}></i>
            </div>
            <div className="form-group top10">
              <input type="text" value={state.phone}
                onChange={(e) => changePhone(e)} placeholder="请输入您的手机号"/>
              <i onClick={() => clearValue()}></i>
            </div>

            <div className="form-group">
              <label onClick={() =>next()}>下一步</label>
            </div>
        </div>
      </div>
  )
}

通过以上方式发现现有的状态管理比以前简单很多

报错信息

store.js:60 A non-serializable value was detected in an action, in the path: register. Value: ƒ register2(key) { _pStore.dispatch({ type: REGISTER,key});}
js 复制代码
import { persistStore, persistReducer, PERSIST} from "redux-persist";
const store = configureStore({
  reducer: reducers,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        // Ignore these action types
        ignoredActions: [PERSIST],
        // Ignore these field paths in all actions
        ignoredActionPaths: [],
        // Ignore these paths in the state
        ignoredPaths: []
      }
      // serializableCheck: false //关闭redux序列化检测
    })
});

一开始 middleware 写错了位置,写在了createSlice中,后来仔细检查代码发现是位置写错了!!!!

生命周期

老版本的生命周期非常清晰,新版版的因为不推荐组件式的组件,推荐的是函数式组件,然后生命周期就非常混乱

老版本比较关注:

  • 首次挂载
  • 卸载前
  • 状态更新的时候

发现 React18.2.0 采用的是函数式组件,并且状态更新的回调已经没有了,所以不能行云流水的在设置卸载前的功能了,挂载后需要执行的操作,并且因为每次状态更新都要重新渲染 DOM ,因此如果在之后的回调中需要设置操作,但是没有状态后的回调了,导致整体逻辑去设置的时候非常混乱,会存在很多坑!!!并且存在什么渲染两次!!!!!!!这些都需要自己考虑,感觉是对用户的维护成本很高

新版本的虽说是可以使用useEffect,但是逻辑非常不清晰,并且官网的手册也根本没有对这些变更给与非常明确的解决方案,感觉?#$%^&*,我们需要花大量的时间去考虑各个变量之间的关系,可以不可以在某个位置更新,反正目前为止在升级的过程中的维护感觉很痛苦,我在考略要不要放弃这个框架了。。。。。。

问题汇总

这里是升级过程中遇到的问题汇总

Module not found: Can't resolve 'web-vitals'

如果启动后发现以上报错信息,说明之前米有初始化该工具包,初始化一下即可

shell 复制代码
npm i web-vitals@2.1.4 -D

Cannot access 'WEBPACK_DEFAULT_EXPORT' before initialization

es6模块循环依赖问题导致,查找代码把循环依赖注释掉即可

State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect()

在更新前,使用this.setState()修改状态,并且this.setState()是异步函数,有回调函数的;

包升级后,使用useState()修改状态,并且useState()是异步函数,但是没有回调函数

Line 296:7: React Hook "useEffect" is called in function "next" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"

这是本人在开发工程中,从网上查找相关资料,说是组件名称没有大写开头,可是本人组件名称是大写!!!!但是提示next不是React组件方法名 ,感觉有点莫名其妙,想到next确实小写,本人将 useEffect 方式写在了函数"next"中,移到组件根块下报错消失!!!!所以useEffect是必须直接使用在组件结构下

相关推荐
问道飞鱼10 分钟前
【前端知识】强大的js动画组件anime.js
开发语言·前端·javascript·anime.js
k093312 分钟前
vue中proxy代理配置(测试一)
前端·javascript·vue.js
傻小胖13 分钟前
React 脚手架使用指南
前端·react.js·前端框架
程序员海军26 分钟前
2024 Nuxt3 年度生态总结
前端·nuxt.js
m0_7482567836 分钟前
SpringBoot 依赖之Spring Web
前端·spring boot·spring
web135085886351 小时前
前端node.js
前端·node.js·vim
m0_512744641 小时前
极客大挑战2024-web-wp(详细)
android·前端
若川1 小时前
Taro 源码揭秘:10. Taro 到底是怎样转换成小程序文件的?
前端·javascript·react.js
潜意识起点1 小时前
精通 CSS 阴影效果:从基础到高级应用
前端·css
奋斗吧程序媛2 小时前
删除VSCode上 origin/分支名,但GitLab上实际上不存在的分支
前端·vscode