SakuraSnow's blog SakuraSnow's blog
首页
  • JavaScript
  • TypeScript
  • Vue
  • React
  • Git
  • Node
  • Linux
  • 技术文档
  • 博客搭建
  • 数据结构
  • leetcode
  • 关于
  • 友链
  • 收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

SakuraSnow

一只前端咸鱼
首页
  • JavaScript
  • TypeScript
  • Vue
  • React
  • Git
  • Node
  • Linux
  • 技术文档
  • 博客搭建
  • 数据结构
  • leetcode
  • 关于
  • 友链
  • 收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 核心概念

  • 高级指引

    • BaKa也能看懂的react-router和redux教程
    • React笔记
    • 高级指引
    SakuraSnow
    2020-08-24

    BaKa也能看懂的react-router和redux教程

    # 序

    这是一个非常基础的redux教程,在我学redux时,发现无论是官方文档还是其他的博客,均会先讲一堆redux的设计思想,然后抛出一堆新概念看着脑壳疼,所以我打算写一篇非常基础的教程,没有花里花哨的,只是讲讲最简单的使用,至于思想啥的,掘金一搜一大把啦。所以这篇文章只是追求用最简单的例子来介绍API,不会讲的太深,就酱。

    # redux

    redux和react没有太大的关系,它只是一个单独的库,你可以随便在哪个地方使用它,它的主要用途我觉得有两点

    • 便于多组件跨级间共享数据
    • 便于提供规范统一的数据源管理

    先用一张图介绍redux中的几个重要角色

    file

    • store:顾名思义,是用来保存数据的
    • action:redux不允许直接操作数据,修改仓库的数据需要派发一个action,这个action用于标识怎么修改数据
    • reducer:reducer用于根据action的类型来对数据进行具体的处理

    我们来看一个简单的例子

    import {createStore} from "redux";
    
    function reducer(state, action) {
        let newState = {
            ...state
        }
        // 根据type对状态进行处理
        switch (action.type) {
            case "add":
                newState.num++
                break;
            default:
                break;
        }
    	// 返回新的状态
        return newState;
    }
    
    // 创建仓库
    let store = createStore(reducer, {
        num : 0
    });
    
    // 数据更新时触发的回调
    const handleStoreUpdate = function () {
        console.log(store.getState());
    }
    
    // 注册数据更新时触发的函数
    store.subscribe(handleStoreUpdate);
    
    
    setTimeout(() => {
        // 派发action修改数据
        store.dispatch({
            type : "add"
        })
    }, 1000)
    
    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
    35
    36
    37
    38

    在使用过程中,先通过createStore创建一个store,传入的两个参数依次是reducer(也就是具体根据action处理数据的方法) 和state的默认值

    然后通过store.subscribe订阅state的变化

    最后我们可以通过store.dispatch派发action,派发的action会被传入到reducer中进行处理

    file

    那么redux的简单使用就介绍完了。

    # react + redux

    这里我们用一个简单的例子来介绍使用,我们要做一个可以通过按+或-来控制上方显示的计数器

    # 安装react-redux

    要把react和redux结合起来,我们要使用一个库react-redux

    安装:npm install react-redux --save

    # 配置store

    const reducer = function (state, action) {
    	let num = state.num;
    	switch (action.type) {
    		case "increase":
    			num++;
    			break;
    		case "decrease":
    			num--;
    			break;
    		default:
    			break;
    	}
    	return {
    		...state,
    		num
    	}
    }
    let store = createStore(reducer, {num : 0});
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    # 在外围组件中配置Provider

    在这里我们在根组件中中配置Provider,也就是把Counter组件放到redux的上下文中

    import React from 'react';
    import ReactDOM from 'react-dom';
    import {store} from "./redux/index.js";
    import {Provider} from "react-redux";
    import Counter from "./components/Test";
    
    const rootElement = document.getElementById('root');
    const renderElement = (
    	<Provider store={store}>
    		<Counter/>
    	</Provider>
    );
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    # 使用connect对redux的组件进行包装

    import React from "react";
    import {connect} from "react-redux";
    
    function Counter(props) {
        return (
            <div>
                <h2>{JSON.stringify(props.num)}</h2>
                <button onClick={props.increase}>+</button>
                <button onClick={props.decrease}>-</button>
            </div>
        )
    }
    
    function mapStateToProps(state) {
        return {
            num : state.num
        }
    }
    function mapDispatchToProps(dispatch) {
        return {
            increase() {
                dispatch({type : "increase"})
            },
            decrease() {
                dispatch({type : "decrease"});
            }
        }
    }
    
    let CounterContainer = connect(mapStateToProps, mapDispatchToProps)(Counter);
    
    export default CounterContainer;
    
    
    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

    这里使用了connect这个函数,这个函数在第一次调用时会返回一个高阶组件用于包装组件,如果你不知道什么是高阶组件也不要紧,你只要记住需要连续调用两次就行了,第一次传入两个函数,第二次传入要包装的组件

    其中第一个函数mapStateToProps用于把store中的state映射到props上,也就是这个函数返回的值会和props先进行一次混合再传给我们的Counter组件,这样我们的Counter组件就可以通过props拿到store中的内容了

    第二个函数mapDispatchToProps用于封装要用到的action,这是为了确保组件里不能乱派发action,所以集合起来统一管理,同样的,返回的对象同样会和props混合后传给Counter组件

    最后我们把包装后的组件导出就好啦

    # react-router + redux

    如果我们要把router和redux中的某些数据进行联动,我们就可以把router的数据放到redux上。

    # 安装

    安装必要的库

    yarn add react-redux
    yarn add react-router
    yarn add connected-react-router
    
    1
    2
    3

    # 配置store

    import thunk from 'redux-thunk';  // 这个中间件可以让action为函数
    import {createBrowserHistory} from 'history';
    import {createStore, applyMiddleware, combineReducers} from 'redux';
    import { connectRouter, routerMiddleware } from 'connected-react-router';
    import { composeWithDevTools } from 'redux-devtools-extension';
    
    export const history = createBrowserHistory();  // 浏览器端应用使用createBrowserHistory来创建history对象
    const initialState = {};
    const reducers = combineReducers({
        router: connectRouter(history),		// 合并router-redux联动需要的reducer
    })
    const store = createStore(
        connectRouter(history)(reducers), // 使用connectRouter包裹reducer
        initialState,
        composeWithDevTools(applyMiddleware(thunk, routerMiddleware(history)))
    	// composeWithDevTools是调试用的中间件(如果你不需要可以去掉)
    	// routerMiddleware让路由通过dispatch(push('/url'))来变化
    );
    
    store.subscribe(() => {
        console.log(store.getState())
    })
    
    export default store
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    # 配置根组件

    import React from 'react'
    import { render } from 'react-dom'
    import { Provider } from 'react-redux'
    import { ConnectedRouter } from 'connected-react-router'
    import "./App.css"
    import App from './App';
    import store from './redux/store';
    import { history } from './redux/store';
    
    // 使用ConnectedRouter代替Router,并且将store中创建的history对象引入
    render(
    	<Provider store={store}>
    		<ConnectedRouter history={history}>
    			<App/>
    		</ConnectedRouter>
    	</Provider>,
    	document.querySelector("#root")
    )
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    # 设置App组件

    import React from 'react'
    import {connect} from "react-redux";
    import {push} from 'connected-react-router';
    import {Route, Switch} from 'react-router'; // react-router v4/v5
    
    function A() {
        return <h1>组件A</h1>
    }
    
    
    function B() {
        return <h1>组件B</h1>
    }
    
    function App(props) {
        return (
            <>
                <Switch>
                    <Route path="/b" component={B} />
                    <Route path="/a" component={A} />
                </Switch>
                <div className={"button-box"}>
                    <button onClick={props.toA}>A</button>
                    <button onClick={props.toB}>B</button>
                </div>
            </>
        )
    }
    
    function mapStateToProps(state) {
        return {
    
        }
    }
    function mapDispatchToProps(dispatch) {
        return {
    		// 使用dispath切换路由
            toA() {
                dispatch(push('/a'))
            },
            toB() {
                dispatch(push('/b'));
            }
        }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(App);
    
    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
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47

    # 测试一下

    我们分别点击按钮A和B

    file

    可以看到router的变化已经被同步到redux了

    #React#前端
    上次更新: 2022/03/05, 15:57:30
    React组件的生命周期

    ← React组件的生命周期

    最近更新
    01
    009-Palindrome Number[回文数]
    03-10
    02
    008-String to Integer (atoi)[字符串转整数]
    03-10
    03
    004-Reverse-integer[整数反转]
    03-09
    更多文章>
    Theme by Vdoing | Copyright © 2019-2022 Evan Xu | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式
    ×