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

  • 23
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
引用:react-router-dom是一个用于React应用程序的路由库。它提供了一种管理应用程序中不同页面之间导航的方式。react-router-dom有几个相关的模块,包括react-router-native、react-router-reduxreact-router-config等。其中,react-router-native是用于React Native应用程序的绑定,react-router-redux是与Redux结合使用的模块,而react-router-config则是一个用于静态路由配置的助手。 引用:在使用react-router-dom的v6版本时,可以通过使用useRoutes函数来绑定路由配置。在一个App组件中,可以导入routes文件,并在App组件中使用useRoutes函数将路由配置渲染到页面上。 引用:在v6版本中,可以选择使用BrowserRouter或HashRouter来包裹整个应用程序的组件。BrowserRouter和HashRouter的作用都是为了管理地址栏的URL,但BrowserRouter使用的是正常的URL路径,而HashRouter修改的是地址栏的hash值。另外,在v6版本中,可以使用<Routes>和<Route>来进行路由配置,其中<Routes>用于包含多个<Route>组件,每个<Route>对应一个页面。 所以,如果你想了解react-router-dom v6的更多内容,可以参考官方文档或查看相关示例代码。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [react-router:react-router 中文文档](https://download.csdn.net/download/weixin_42166261/18230223)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [react-router-dom V6 中文文档教程总结](https://blog.csdn.net/xm1037782843/article/details/127454966)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [React学习笔记_React Router 6](https://blog.csdn.net/qq_20470063/article/details/123361115)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三知之灵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值