• React+TS学习和使用(三):React Redux和项目的路由配置


    开启学习react+ts,本篇主要是学习使用React Redux和项目的路由配置

    一、React Redux

    需求:使用TS+React Redux实现一个累加。

    A. 安装

    $ yarn add redux react-redux redux-devtools-extension
    
    
    • 1
    • 2

    B. Store

    src下新建 store 目录,在其中新建 reducer.tsindex.ts

    a. reducer

    const defaultState = {
        num: 1
    }
    
    interface IAction {
        type: string;
        value: number;
    }
    
    // eslint-disable-next-line
    export default (state=defaultState, action: IAction) => {
        let newState = JSON.parse(JSON.stringify(state));
        switch(action.type){
            case "increase":
                newState.num+=action.value;
                break;
            default:
                break;
        }
        return newState;
    }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    b. store

    import {applyMiddleware, createStore} from 'redux'
    import reducer from './reducer'
    import {composeWithDevTools} from 'redux-devtools-extension'
    
    const store = createStore(reducer, composeWithDevTools(applyMiddleware()))
    export default store;
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    C. Provider

    在入口文件 index.tsx 中:

    import ReactDOM from 'react-dom'
    import App from './App4'
    import {Provider} from 'react-redux'
    import store from 'store'
    
    ReactDOM.render(
        <Provider store={store}>
            <App />
        </Provider>,
        document.getElementById("root")
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    D. connect

    在组件中:

    import {connect} from 'react-redux'
    import React from 'react'
    import {Dispatch} from 'redux'	// redux提供了Dispatch作为dispatch的类型检测接口
    
    interface IProps {
        num: number;
        increaseFn: ()=>void
    }
    
    const App4: React.FC<IProps> = (props) => {
        return (
            <div>
                <h3>{props.num}</h3>
                <button onClick={()=>props.increaseFn()}>累加</button>
            </div>
        )
    }
    
    const mapStateToProps = (state: {num: number}) => {
        return {
            num: state.num
        }
    }
    
    const mapDispatchToProps = (dispatch: Dispatch) => {
        return {
            increaseFn(){
                dispatch({type: "increase", value: 2})
            }
        }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(App4)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    五、路由

    1、安装路由

    本课程使用目前最新版的React路由(v6) (opens new window)

    $ npm install react-router-dom@6
    # 或者
    $ yarn add react-router-dom@6
    
    
    • 1
    • 2
    • 3
    • 4

    注意:

    版本会随时更新,因此请指定版本安装。

    2、路由配置

    a. 路由创建

    src 下创建 router>index.tsx。以首页与登录页切换为例:

    import App from "App6";
    import Home from "Home";
    import List from "List";
    import Detail from "Detail";
    import About from "About";
    import Login from "Login";
    import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
    
    const MyRouter = () => (
      <Router>
        <Routes>
          <Route path="/" element={<App />}>
            <Route index element={<Home />}></Route>
            <Route path="/list" element={<List />}></Route>
            <Route path="/detail" element={<Detail />}></Route>
            <Route path="/about" element={<About />}></Route>
          </Route>
          <Route path="/login" element={<Login />}></Route>
        </Routes>
      </Router>
    );
    
    export default MyRouter;
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    关键词解析:

    1、BrowserRouter重命名为Router

    2、所有的Route组件必须放在Routes组件中

    3、Route标签上的element属性必须填写标签结构的组件,如:,而不是 Home

    4、加了index属性的路由不需要写path,因为/路径就指向该组件

    b. 入口文件引入路由

    src>index.tsx

    import ReactDOM from 'react-dom'
    import MyRouter from 'router'
    
    ReactDOM.render(
        <MyRouter />,
        document.getElementById("root")
    )
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    c. 组件显示

    App.tsx 中:

    import React from "react";
    import { Outlet, Link } from "react-router-dom";
    
    function App() {
      return (
        <div>
          <ul>
            <li><Link to={"/list"}>列表页</Link></li>
            <li><Link to={"/detail"}>详情页</Link></li>
            <li><Link to={"/about"}>关于我们</Link></li>
          </ul>
          <Outlet />
        </div>
      );
    }
    
    export default App;
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    关键词解析:

    1、 组件用来显示子路由,类似于Vue的

    2、Link最终会被html解析为a标签

    目前结合ts的情况下,无法使用index属性指定首页组件,因此如果希望 / 跳转 /home,需要:

    import { useLocation } from "react-router-dom";
    
    let { pathname } = useLocation();
    useEffect(() => {
        if (pathname === "/") {
            navigate("/home");
        }
    }, []);
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3、参数获取

    a. 子路由形式携带

    路由跳转往往伴随着参数的传递,假如:

    // 登录页的路由配置
    <Route path="/login/:id" element={<Login />}></Route>
    
    // Link跳转路由
    <Link to="/login/123">登录页</Link>
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    此时可以使用React Router Dom提供的Hook来获取:

    import { useParams } from 'react-router-dom'
    
    // 从路由参数中解构出来
    const {id} = useParams()
    console.log(id)    // 123
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    b. 问号(?)形式参数

    // 登录页的路由配置
    <Route path="/login" element={<Login />}></Route>
    
    // Link跳转路由
    <Link to="/login?id=123">登录页</Link>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    获取形式:

    import { useSearchParams } from 'react-router-dom'
    
    const [params] = useSearchParams()
    console.log(params.getAll('id'))    // ['123']
      
    
    • 1
    • 2
    • 3
    • 4
    • 5

    以上的id其实属于携带方式不明确,也不一定会携带,因此路由可以设置为:

    <Route path="/login/*" element={<Login />}></Route>
    
    
    • 1
    • 2

    4、事件跳转

    事件中执行跳转页面,可以使用useNavigate这个hook进行跳转。

    import { useNavigate } from "react-router-dom";
    
    const navigate = useNavigate();
    const goLogin = () => {
        navigate('/login')
    }
    
    <span onClick={goLogin}>登录页2</span>
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    简单参数的传递可以直接带在url后,而复杂参数需要以复杂数据类型的形式携带:

    const navigate = useNavigate();
    navigate('/login', {state: {id: 456}})
        
    
    • 1
    • 2
    • 3

    注意:

    navigate方法第二个参数必须是对象,而且这个对象只接受replace和state两个属性,state可以用来携带参数。

    携带复杂参数,可以使用useLocation来获取参数:

    const location = useLocation()
    console.log(location.state.id);  // 456
      
    
    • 1
    • 2
    • 3

    注意:

    这里如果使用了TS,那么location会报错,因为其中的state属于不确定的类型,因此没办法直接location.state调用。解决方法有两个:一是单独设置state字段为any,二是直接设置location类型为any。

    // 方法一:设置state为any
    interface ILocation {
        state: any,
        search: string,
        pathname: string,
        key: string,
        hash: string
    }
    
    const location: ILocation = useLocation()
    
    // 方法二:设置location为any
    const location: any = useLocation()
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    5、404匹配

    当路由为404时,可以对路由文件 router/index.tsx 进行如下匹配:

    ...
    import NoMatch from "NoMatch";
    import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
    
    const MyRouter = () => (
      <Router>
        <Routes>
          <Route path="/" element={<App />}>
            ...
          </Route>
          <Route path="/login" element={<Login />}></Route>
          <Route path="*" element={<NoMatch />}></Route>
        </Routes>
      </Router>
    );
    
    export default MyRouter;
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如此,输入错误路径,就会自动重定向到404页面了。

  • 相关阅读:
    盲人公园游玩:科技之翼助力视障人士畅享户外乐趣
    20220625阶段总结
    【Flink】窗口(Window)
    HTTP协议
    ZYNQ双核启动和固化步骤
    优思学院|六西格玛在摩托罗拉的故事回顾-2022
    公共建筑节能大数据应用进展
    Hook原理--逆向开发
    学习Git看这一篇就够了
    高等教育学:教学理论
  • 原文地址:https://blog.csdn.net/aiyang1214878408/article/details/125450615