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

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

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

环境

  • 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中了

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

//老版
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

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

//老版
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

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

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文件中设置,也可以单独设置

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编写的,写法更加方便简洁

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

//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

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中引入路由拦截编写的,该方式写法更加方便简洁

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,实现跳转的方法:

let {
   history} = this.props;
history.push('/detail')

新版本跳转方法:

import {
    useNavigate } from "react-router-dom";
const navigate = useNavigate();
navigate("/detail")

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

标签跳转

   <Route path="/" element={
   <Navigate to="/index" replace />}/>

状态管理

React 状态管理–Redux4.0.4版本

  • “redux”: “^4.0.4”
  • “react-redux”: “^7.1.0”

action.js定义改变状态类型

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定义组件需要修改的全局变量

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 定义改变状态类型

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 定义所有的全局变量

import {
   createStore} from 'redux'
import {
   allReducer} from './redux'
export const store = createStore(allReducer)

App.js 全局变量的注册:

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;

组件使用全局变量

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 定义所有全局状态

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 定义所有全局状态–持久化版本

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

npm i redux-persist

其次,修改相关代码

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 };

组件使用全局变量:

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});}
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’

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

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是必须直接使用在组件结构下

相关推荐

  1. [react]react-router-dom redux 版本升级

    2024-01-01 12:14:02       55 阅读
  2. react项目中的redux以及react-router-dom

    2024-01-01 12:14:02       46 阅读
  3. React Router DOM BrowserRouter

    2024-01-01 12:14:02       36 阅读
  4. 使用 React Router Dom 实现路由导航

    2024-01-01 12:14:02       42 阅读
  5. Redux状态管理原理Redux Toolkit使用教程

    2024-01-01 12:14:02       40 阅读
  6. HOW - React Router v6.x Feature 实践(react-router-dom

    2024-01-01 12:14:02       25 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-01 12:14:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-01 12:14:02       101 阅读
  3. 在Django里面运行非项目文件

    2024-01-01 12:14:02       82 阅读
  4. Python语言-面向对象

    2024-01-01 12:14:02       91 阅读

热门阅读

  1. MATLAB常用笔记记录(持续更新)

    2024-01-01 12:14:02       75 阅读
  2. Python基础中的列表知识整理归纳

    2024-01-01 12:14:02       59 阅读
  3. 10 HXCodec

    2024-01-01 12:14:02       51 阅读
  4. 17. Mysql 动态SQL

    2024-01-01 12:14:02       58 阅读
  5. leetcode中shell题解

    2024-01-01 12:14:02       61 阅读
  6. Spring Boot Actuator 功能介绍

    2024-01-01 12:14:02       47 阅读