内容简介:Redux 和 React-redux 并不是同一个东西。Redux 是一种架构模式(Flux 架构的一种变种),它不关注你到底用什么库,你可以把它应用到 React 和 Vue,甚至跟 jQuery 结合都没有问题。模块(组件)之间需要共享数据,但是每个人都可以修改它,
Redux 和 React-redux 并不是同一个东西。
Redux 是一种架构模式(Flux 架构的一种变种),它不关注你到底用什么库,你可以把它应用到 React 和 Vue,甚至跟 jQuery 结合都没有问题。
而 React-redux 就是把 Redux 这种架构模式和 React.js 结合起来的一个库,就是 Redux 架构在 React.js 中的体现。
为什么要用redux来管理数据
模块(组件)之间需要共享数据,但是每个人都可以修改它, 一个可以被不同模块任意修改共享的数据状态就是魔鬼,一旦数据可以任意修改,所有对共享状态的操作都是不可预料的 ,数据可能被任意修改导致不可预料的结果。
一、先来学习redux
dispatch
我们定义一个函数,叫 dispatch,它专门负责数据的修改:
function dispatch (action) { switch (action.type) { case 'UPDATE_TITLE_TEXT': appState.title.text = action.text break case 'UPDATE_TITLE_COLOR': appState.title.color = action.color break default: break } } 复制代码
所有对数据的操作必须通过 dispatch 函数。它接受一个参数 action,这个 action 是一个普通的 JavaScript 对象,里面必须包含一个 type 字段来声明你到底想干什么。
store
const store = createStore(appState, stateChanger) store.subscribe(() => renderApp(store.getState())) // 监听数据变化 renderApp(store.getState()) // 首次渲染页面 store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: '《React.js 小书》' }) // 修改标题文本 store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' }) // 修改标题颜色 复制代码
通过 store.getState 我们获取共享状态,而且我们约定只能通过 store.dispatch 修改共享状态。store 也允许我们通过 store.subscribe 监听数据数据状态被修改了,并且进行后续的例如重新渲染页面的操作。
reducer
createStore 接受一个叫 reducer 的函数作为参数,这个函数规定是一个纯函数,它接受两个参数,一个是 state,一个是 action。
reducer 是不允许有副作用的。你不能在里面操作 DOM,也不能发 Ajax 请求,更不能直接修改 state,它要做的 仅仅是 —— 负责初始 state,和根据 state 和 action 计算具有共享结构的新的 state。
总的来说redux的结构是这样
const reducer = (state = {count: 0}, action) => { switch (action.type){ case 'INCREASE': return {count: state.count + 1}; case 'DECREASE': return {count: state.count - 1}; default: return state; } } const actions = { increase: () => ({type: 'INCREASE'}), decrease: () => ({type: 'DECREASE'}) } const store = createStore(reducer); store.subscribe(() => console.log(store.getState()) ); store.dispatch(actions.increase()) // {count: 1} store.dispatch(actions.increase()) // {count: 2} store.dispatch(actions.increase()) // {count: 3} 复制代码
通过 reducer 创建一个 store,每当我们在 store 上 dispatch 一个 action,store 内的数据就会相应地发生变化。
创建store的时候,要先创建reducer,把reducer传递进store下的index.js reducer里放的就是存放的数据
reducer可以接收state,但是绝不能修改state
1、store必须是唯一的
2、只有store能够改变自己的内容,reducer不能改
3、reducer必须是纯函数(给定固定的输入,就一定会有给定的输出,return的值是一定的)即不能有异步操作,也不能有关于时间定时器之类的操作
API
store.dispatch:派发action,帮助传递给store
store.getState:获取store中的数据
store.subscribe:监听数据的改变,触发监听回调
接下来就是react-redux出场了
首先在最外层容器中,把所有内容包裹在 Provider 组件中,将之前创建的 store 作为 prop 传给 Provider。
const App = () => { return ( <Provider store={store}> <Comp/> </Provider> ) }; 复制代码
Provider 内的任何一个组件(比如这里的 Comp),如果需要使用 state 中的数据,就必须是「被 connect 过的」组件——使用 connect 方法对「你编写的组件(MyComp)」进行包装后的产物。
class MyComp extends Component { // content... } const Comp = connect(...args)(MyComp); 复制代码
可见,connect 方法是重中之重。
connect 详解
connect() 接收四个参数,它们分别是 mapStateToProps,mapDispatchToProps,mergeProps和options。
mapStateToProps(state, ownProps) : stateProps
这个函数允许我们将 store 中的数据作为 props 绑定到组件上。
const mapStateToProps = (state) => { return { count: state.count } } 复制代码
这个函数的第一个参数就是 Redux 的 store,我们从中摘取了 count 属性。因为返回了具有 count 属性的对象,所以 MyComp 会有名为 count 的 props 字段。
class MyComp extends Component { render(){ return <div>计数:{this.props.count}次</div> } } const Comp = connect(...args)(MyComp); 复制代码
当然,你不必将 state 中的数据原封不动地传入组件,可以根据 state 中的数据,动态地输出组件需要的(最小)属性。
const mapStateToProps = (state) => { return { greaterThanFive: state.count > 5 } } 复制代码
函数的第二个参数 ownProps,是 MyComp 自己的 props。有的时候,ownProps 也会对其产生影响。比如,当你在 store 中维护了一个用户列表,而你的组件 MyComp 只关心一个用户(通过 props 中的 userId 体现)。
const mapStateToProps = (state, ownProps) => { // state 是 {userList: [{id: 0, name: '王二'}]} return { user: _.find(state.userList, {id: ownProps.userId}) } } class MyComp extends Component { static PropTypes = { userId: PropTypes.string.isRequired, user: PropTypes.object }; render(){ return <div>用户名:{this.props.user.name}</div> } } const Comp = connect(mapStateToProps)(MyComp); 复制代码
当 state 变化,或者 ownProps 变化的时候,mapStateToProps 都会被调用,计算出一个新的 stateProps,(在与 ownProps merge 后)更新给 MyComp。
这就是将 Redux store 中的数据连接到组件的基本方式。
mapDispatchToProps(dispatch, ownProps): dispatchProps
connect 的第二个参数是 mapDispatchToProps,它的功能是,将 action 作为 props 绑定到 MyComp 上。
const mapDispatchToProps = (dispatch, ownProps) => { return { increase: (...args) => dispatch(actions.increase(...args)), decrease: (...args) => dispatch(actions.decrease(...args)) } } class MyComp extends Component { render(){ const {count, increase, decrease} = this.props; return (<div> <div>计数:{this.props.count}次</div> <button onClick={increase}>增加</button> <button onClick={decrease}>减少</button> </div>) } } const Comp = connect(mapStateToProps, mapDispatchToProps)(MyComp); 复制代码
由于 mapDispatchToProps 方法返回了具有 increase 属性和 decrease 属性的对象,这两个属性也会成为 MyComp 的 props。
如上所示,调用 actions.increase() 只能得到一个 action 对象 {type:'INCREASE'},要触发这个 action 必须在 store 上调用 dispatch 方法。diapatch 正是 mapDispatchToProps 的第一个参数。但是,为了不让 MyComp 组件感知到 dispatch 的存在,我们需要将 increase 和 decrease 两个函数包装一下,使之成为直接可被调用的函数(即,调用该方法就会触发 dispatch)。
Redux 本身提供了 bindActionCreators 函数,来将 action 包装成直接可被调用的函数。
import {bindActionCreators} from 'redux'; const mapDispatchToProps = (dispatch, ownProps) => { return bindActionCreators({ increase: action.increase, decrease: action.decrease }); } 复制代码
同样,当 ownProps 变化的时候,该函数也会被调用,生成一个新的 dispatchProps,(在与 statePrope 和 ownProps merge 后)更新给 MyComp。注意,action 的变化不会引起上述过程,默认 action 在组件的生命周期中是固定的。
[mergeProps(stateProps, dispatchProps, ownProps): props]
之前说过,不管是 stateProps 还是 dispatchProps,都需要和 ownProps merge 之后才会被赋给 MyComp。connect 的第三个参数就是用来做这件事。通常情况下,你可以不传这个参数,connect 就会使用 Object.assign 替代该方法。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。