序
这是一个非常基础的redux教程,在我学redux时,发现无论是官方文档还是其他的博客,均会先讲一堆redux的设计思想,然后抛出一堆新概念看着脑壳疼,所以我打算写一篇非常基础的教程,没有花里花哨的,只是讲讲最简单的使用,至于思想啥的,掘金一搜一大把啦。所以这篇文章只是追求用最简单的例子来介绍API,不会讲的太深,就酱。
redux
redux和react没有太大的关系,它只是一个单独的库,你可以随便在哪个地方使用它,它的主要用途我觉得有两点
- 便于多组件跨级间共享数据
- 便于提供规范统一的数据源管理
先用一张图介绍redux中的几个重要角色
- store:顾名思义,是用来保存数据的
- action:redux不允许直接操作数据,修改仓库的数据需要派发一个action,这个action用于标识怎么修改数据
- reducer:reducer用于根据action的类型来对数据进行具体的处理
我们来看一个简单的例子
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
| import {createStore} from "redux";
function reducer(state, action) { let newState = { ...state } 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(() => { store.dispatch({ type : "add" }) }, 1000)
|
在使用过程中,先通过createStore
创建一个store,传入的两个参数依次是reducer(也就是具体根据action处理数据的方法) 和state的默认值
然后通过store.subscribe
订阅state的变化
最后我们可以通过store.dispatch
派发action,派发的action会被传入到reducer中进行处理
那么redux的简单使用就介绍完了。
react + redux
这里我们用一个简单的例子来介绍使用,我们要做一个可以通过按+或-来控制上方显示的计数器
安装react-redux
要把react和redux结合起来,我们要使用一个库react-redux
安装:npm install react-redux --save
配置store
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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});
|
在外围组件中配置Provider
在这里我们在根组件中中配置Provider,也就是把Counter组件放到redux的上下文中
1 2 3 4 5 6 7 8 9 10 11 12
| 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> );
|
使用connect对redux的组件进行包装
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
| 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;
|
这里使用了connect
这个函数,这个函数在第一次调用时会返回一个高阶组件用于包装组件,如果你不知道什么是高阶组件也不要紧,你只要记住需要连续调用两次就行了,第一次传入两个函数,第二次传入要包装的组件
其中第一个函数mapStateToProps
用于把store中的state映射到props上,也就是这个函数返回的值会和props先进行一次混合再传给我们的Counter组件,这样我们的Counter组件就可以通过props拿到store中的内容了
第二个函数mapDispatchToProps
用于封装要用到的action,这是为了确保组件里不能乱派发action,所以集合起来统一管理,同样的,返回的对象同样会和props混合后传给Counter组件
最后我们把包装后的组件导出就好啦
react-router + redux
如果我们要把router和redux中的某些数据进行联动,我们就可以把router的数据放到redux上。
安装
安装必要的库
1 2 3
| yarn add react-redux yarn add react-router yarn add connected-react-router
|
配置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 thunk from 'redux-thunk'; 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(); const initialState = {}; const reducers = combineReducers({ router: connectRouter(history), }) const store = createStore( connectRouter(history)(reducers), initialState, composeWithDevTools(applyMiddleware(thunk, routerMiddleware(history))) );
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
| 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';
render( <Provider store={store}> <ConnectedRouter history={history}> <App/> </ConnectedRouter> </Provider>, document.querySelector("#root") )
|
设置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
| import React from 'react' import {connect} from "react-redux"; import {push} from 'connected-react-router'; import {Route, Switch} from 'react-router';
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 { toA() { dispatch(push('/a')) }, toB() { dispatch(push('/b')); } } }
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
测试一下
我们分别点击按钮A和B
可以看到router的变化已经被同步到redux了