安装使用
-
运行命令
yarn add redux
redux 原理图
主要流程
- 组件 通过
Action Creators
分发(dispatch
)action
给Store
Store
将 之前的状态(previousState
)、action
,传递给Reducers
Reducers
对状态处理后,返回给Store
新的状态- 组件通过
getState()
获取状态
各部分职责
Action Creators
: 负责生成action
对象,并分发给Store
Store
:负责管理数据,并将action
传递给Reducers
Reducers
: 对数据进行处理、初始化,并返回给Store
简易demo
-
目录结构
-
/redux/store.js
import {createStore} from 'redux' import test from './test-reducer' export default createStore(test) // 接收 reducers,返回 store
-
/redux/test-reducer.js
const initState = 0 // 初始化时,preState为undefined,因此使用默认值 export default function handleCount (preState=initState, action) { const {type, data} = action switch (type) { case 'increment': // 根据对应 type 去修改状态 return preState + data // 返回值,作为新状态 case 'decrement': return preState - data default: return preState // 返回默认值,即 initState 即 0 } }
-
/components/Count/index.jsx
import React, { Component } from 'react' import store from '../../redux/store' export default class Count extends Component { handleAdd = () => { // 通过 dispatch ,分发修改数据的动作(action) store.dispatch({type: 'increment', data: 1}) } handleSub = () => { store.dispatch({type: 'decrement', data: 1}) } render() { return ( <div> <p>{store.getState()}</p> {/* 使用getState获取状态 */} <button onClick={this.handleAdd}>+1</button> <button onClick={this.handleSub}>-1</button> </div> ) } }
-
App.js
import React, { Component } from 'react' import Count from './components/Count' export default class App extends Component { render() { return ( <div> <Count /> {/*使用 Count*/} </div> ) } }
-
index.js
-
因为
redux
,只负责管理数据,不负责页面更新,因此,当数据变化时,需要自己去更新 -
方法1,使用
store.subscribe
订阅状态变动,一旦变动,在组件中调用this.setState({})
,触发组件更新 -
方法2,使用
store.subscribe
订阅状态变动,一旦变动,重新渲染App
组件,一劳永逸import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import store from './redux/store' ReactDOM.render(<App />, document.getElementById('root')); // 使用方法二,当状态变化时,直接重新渲染App组件 store.subscribe(()=>{ ReactDOM.render(<App />, document.getElementById('root')); })
-
效果
异步 action
-
store.dispatch(action)
可以传入object
或者function
-
如果 传入的
action
是object
,那么称为 同步action
-
如果 传入的
action
是function
,那么称为异步action
-
但传入的
action
如果是function
,store
无法理解,需要使用redux-thunk
中间件 -
安装
redux-thunk
yarn add redux-thunk
-
然后在
store
里应用中间件import {createStore, applyMiddleware} from 'redux' import thunk from 'redux-thunk' // 引入 thunk 中间件 import test from './test-reducer' export default createStore(test, applyMiddleware(thunk))
-
这样,
store
在收到function
类型的action
之后,就可以理解了 -
store
会调用这个function action
,并传入dispatch
方法 -
然后,我们可以在这个函数里面,写一些定时任务,或者
ajax
请求,但一般还是会调用dispatch
去分发一个普通的object action
,来操作数据更新
使用 react-redux
-
redux
不是facebook
官方出品,为了让程序员更好的在项目中使用redux
,官方出品了react-redux
-
安装
react-redux
yarn add react-redux
-
在
react-redux
中,将组件分为两类:UI
组件、容器组件 -
其中,
UI
组件,不涉及redux
相关代码,由 容器组件充当桥梁进行沟通,如下:
-
在以上的demo基础上,进行修改:
-
目录结构:
-
components\Count\index.jsx
// 没有与 redux 相关的代码 import React, { Component } from 'react' export default class Count extends Component { handleAdd = () => { this.props.increment(1) } handleSub = () => { this.props.decrement(1) } render() { return ( <div> <p>{this.props.count}</p> <button onClick={this.handleAdd}>+1</button> <button onClick={this.handleSub}>-1</button> </div> ) } }
-
containers/Count/index.jsx
import CountUI from '../../components/Count' import {connect} from 'react-redux' // 返回值作为 props 传递给 ui组件,一般用来传递 redux 中的状态 const mapStateToProps = (state) => { return { count: state } } // 返回值作为 props 传递给 ui组件,一般用来传递操作状态的方法 const mapDispatchToProps = (dispatch) => { return { increment: (data)=>dispatch({type:'increment', data}), decrement: (data)=>dispatch({type:'decrement', data}) } } // connect 用于连接 UI组件 跟 容器组件 export default connect(mapStateToProps, mapDispatchToProps)(CountUI) // mapDispatchToProps 还可以是一个对象 /* { increment: createIncrementAction, // 值为创建action的函数,react-redux 会执行这个函数,获取action,并自动 dispatch decrement: createDecrementAction } */
-
redux\store.js
// 未变动 import {createStore, applyMiddleware} from 'redux' import thunk from 'redux-thunk' import test from './test-reducer' export default createStore(test, applyMiddleware(thunk))
-
redux\test-reducer.js
// 未变动 const initState = 0 export default function handleCount (preState=initState, action) { const {type, data} = action switch (type) { case 'increment': return preState + data case 'decrement': return preState - data default: return preState } }
-
App.js
import React, { Component } from 'react' import Count from './containers/Count' // 引用并使用 容器组件 import store from './redux/store' export default class App extends Component { render() { return ( <div> // 传递 store 给 容器组件:连接容器组件 跟 redux <Count store={store}/> </div> ) } }
-
index.js
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; // import store from './redux/store' ReactDOM.render(<App />, document.getElementById('root')); /* 使用 react-redux 后,不需要再自己更新页面 store.subscribe(()=>{ ReactDOM.render(<App />, document.getElementById('root')); }) */
-
效果:
使用 Provider
-
在使用
react-redux
时,我们需要通过props
将store
,传递给容器组件 -
但如果容器组件有很多,每个都需要传
store
,那么对我们来说,会很麻烦 -
因此,使用
Provider
,并给Provider
传递store
,那么Provider
会自动传递store
给所有 容器组件 -
App.js
import React, { Component } from 'react' import Count from './containers/Count' export default class App extends Component { render() { return ( <div> <Count/> {/* 不需要自己再传递 store */} </div> ) } }
-
index.js
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import store from './redux/store' import {Provider} from 'react-redux' // 借助 Provider 统一传递 ReactDOM.render( <Provider store={store}> <App /> </Provider> , document.getElementById('root'));
整合UI组件跟容器组件
- 因为
UI
组件并不需要我们渲染,而是直接由react-redux
的connect
方法,生成容器组件 - 所以,我们常常将
UI
组件 跟 容器组件 写成一个 文件,仅暴露容器组件即可,如下: - 通常 整合后的组件,放在
containers
文件夹 /containers/Count/index.jsx
import {connect} from 'react-redux' import React, { Component } from 'react' // UI 组件 class Count extends Component { handleAdd = () => { this.props.increment(1) } handleSub = () => { this.props.decrement(1) } render() { return ( <div> <p>{this.props.count}</p> <button onClick={this.handleAdd}>+1</button> <button onClick={this.handleSub}>-1</button> </div> ) } } // 容器组件 const mapStateToProps = (state) => { return { count: state } } const mapDispatchToProps = (dispatch) => { return { increment: (data)=>dispatch({type:'increment', data}), decrement: (data)=>dispatch({type:'decrement', data}) } } export default connect(mapStateToProps, mapDispatchToProps)(Count)