Redux学习笔记

Redux学习笔记 

newtwg@qq.com 最后修改2020.02.20


React技术出来好多年了,一直没有好好学习, 近年来做项目一直都在用vue。

这次将要做个基于React-Redux的源码二次开发项目,所以不得不好好学习了。

趁着宅家,我手上拿着书,在github与csdn中摸爬打滚,已经奋战了四五天,一阵恶补后总算对React有了个初步的认识。

这里简单做个记录,也许也能助后来者更快入门。

其中不免理解有误之处,还请多多指正。

(一)Flux概念

An application architecture for React utilizing a unidirectional data flow.

 

(二)Redux概念

A Predictable State Container for JS Apps

 

Redux是一种对Flux的优化实现

 

(三)使用纯Redux

https://github.com/reduxjs/redux/tree/master/examples/counter

1.通过<script>引用Redux.min.js

2.定义reducer:

理解:即定义实际的数据与操作,要确定2项内容:

(1)对state数据的操作

根据store.dispatch过来的action进行操作

(2)返回操作后的state数据

function myReducer(state=initState, action) {
    switch (action.type) {
    ...
    default:
        return state;
    }
}

3.创建store实例(以定义的reducer为参数)

理解:即把reducer封装成store实例,后面引用store就可以完成所有事情

var store = Redux.createStore(myReducer)

4.注册store内state改变时的事件函数(如重新渲染)

理解:store.dispatch()时会调度store中关联的reducer对state作出改变,随后调用store.suscribe()注册的事件函数

store.subscribe(render)

5.在view中创建action,并通过store.dispatch调度

理解:调度的目的是让store中的reducer对state作出修改

store.dispatch({ type: 'ACTION_NAME',,data:...})

view中可以用store.getState()来获得store中的state数据

6.要点小结:

(1)核心就是store对象,其中包括:

①内容: reducer, 定义了数据的结构与操作逻辑

②入口: store.dispatch()

③产出: state, 在view中通过store.getState()获取

(2)代码要点

function myReducer(state=initState, action)
var store = Redux.createStore(myReducer)
store.subscribe(render)
store.dispatch({ type: 'ACTION_NAME',,data:...})
store.getState()

 

(四)配合React使用

 

https://github.com/reduxjs/redux/tree/master/examples/counter

一图看懂(by newtwg):示例工程项目结构及代码关键点

 

1.通过import引用redux

import { createStore } from 'redux'

2.定义reducer:

一般在reducers中创建单独的文件来定义,定义方式之前类似:

export default (state=0,action)=>{
  switch (action.type) {
   ...
  default:
    return state
  }
}

然后在创建store的js文件中通过import引用

3.创建store实例(以定义的reducer为参数)

import counter from './reducers'
const store = createStore(counter)

4.将store(或其部分)作为props传给组件,可能需要逐级下传

<Counter
    value={store.getState()}
    onIncrement={()=>store.dispatch({type:'INCREMENT'})}
    onDecrement={()=>store.dispatch({type:'DECREMENT'})}
  />,

5.注册store发生改变时的事件函数(如重新渲染)

理解:store.dispatch()时会调度store中关联的reducer对state作出改变,随后调用store.suscribe()注册的事件函数

store.subscribe(render)

6.要点小结:

(1)store对象应放在上层的入口文件中,通过props逐级下传

(2)reducers文件夹可用于组织多个reducer文件

(3)代码要点

import { createStore } from 'redux'
import counter from './reducers'
const store = createStore(counter)
<Counter value={store.getState()} onIncrement={()=>store.dispatch({type:'INCREMENT'})} onDecrement={()=>store.dispatch({type:'DECREMENT'})}/>,
store.subscribe(render)

 

 (五)通过React-Redux使用

Official React bindings for Redux

主要是解决在React组件中Redux的store(state与dispatch)需要层层传递的问题

https://github.com/reduxjs/redux/tree/master/examples/todomvc

一图看懂(by newtwg):示例工程项目结构及代码关键点

 

1.定义reducer:

可以在src/reducers文件夹中创建多个单独的reducer文件(定义方式和之前类似),然后在src/reducers/index.js中用redux的combineReducers()合并,生成多个state数据节点:

import { combineReducers } from 'redux'
import todos from './todos'
import visibilityFilter from './visibilityFilter'
const rootReducer = combineReducers({
  todos, visibilityFilter
})
export default rootReducer

这里想说一点,关于这里的reducer这个单词,网上好多地方都直接按字面翻译成缩减器、还原器或减速器之类,我认为有些不妥,这很容易让人费解。reduce有分解归纳之意,比如译作分解归纳器也许更恰当一些,因为它完成的功能正是把redux的store中所有可预见性的数据及其操作进行分解和归纳。(个人见解仅供参考了)

2.入口文件中:

(1)创建store实例(以定义的reducer为参数)

import { createStore } from 'redux'
import counter from './reducers' //或写全 './reducers/index.js'
const store = createStore(counter)

(2)用Provider对原根组件进行包装,并传入store

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

3.不直接使用原组件,需以connect函数封装成高阶组件再调用

(1)对于有读取或操作数据需求的组件,都要进行封装,以传入state和dispatch

(2)原组件不管在view中使用时,处在多深的层级,只要封装后,都可以通过props引用传入的state和dispatch

(3)原组件文件夹为src/components, 高阶容器组件文件夹为src/containers

//导入需要读取和操作数据的原组件MainSection
import MainSection from '../components/MainSection'
const mapStateToProps = state => ({
  todos: state.todos,
  filter: state.visibilityFilter
})
const mapDispatchToProps = dispatch => ({
  actions:{
    action1:()=>dispatch({type:'ACTION_NAME1'}),
    action2:(v)=>dispatch({type:'ACTION_NAME2',val:v})
  }
})
export default connect(
  mapStateToProps, mapDispatchToProps
)(MainSection) //导出对原组件进行封装后的新组件

4.在原组件中(不管层级多深)可通过props(可配合ES6解构)引用state和dispatch

const MainSection=({todos,filter,actions})=>(
    //原组件中可以:
        //用todos和filter读取数据
        //用actions.acton1()、actions.acton2(v) 发送操作调度
)
export default MainSection;

5.要点小结:

(1)store对象应放在上层的入口文件中,通过props逐级下传

(2)reducers文件夹可用于组织多个reducer文件

(3)代码要点

export default combineReducers({todos, visibilityFilter})
import reducer from './reducers'
const store = createStore(reducer)
<Provider store={store}><App /></Provider>
export default connect(mapStateToProps, mapDispatchToProps)(MainSection)

 (六)配合redux-thunk使用Redux 

Thunk middleware for Redux

https://github.com/reduxjs/redux-thunk

 

 1.为什么要用redux-thunk

主要是解决在Redux中异步调度(或有条件调度)的问题,

(1)不用中间件的实现方式:

①在组件中单独进行异步及条件调度

弊端:在每个组件中都要单独实现一次

②在用react-redux的connect()封装时处理异步及条件调度

通过第2个参数mapDispatchToProps是型如function(dispatch,ownProps)的这个机制,可以用这个dispatch发起异步调度

const mapDispatchToProps = (dispatch, ownProps)  => ({
    action1 : (v)=>{
        setTimeout(()=>{
            dispatch({type : 'ACTION_NAME1',val : v});
        },1000)
    }
})
export default connect(null, mapDispatchToProps)(BaseComponent)

弊端:在每个connect封装前都要单独实现一次

(2)使用redux-thunk的实现方式

但如果调度的这个action本身就始终是一个异步的或有条件的操作怎么办,在每个react-redux的conect()前(甚至在组件中)敲一遍处理代码显然太麻烦,不利于后期业务变更时进行维护。

当然还有一种方式理论上应该可以,就是在reducer中处理。但reducerk中本应该就是对state的纯数据操作,不宜有太多的业务逻辑,以reducer中处理有违react的设计理念,主要是会大大增加耦合程度,从而使系统后期变得难以维护。

而这时,我们使用redux-thunk这个中间件,就可以从store.dispatch()的参数action本身入手,使store.dispatch不但可以接受plain objects类型的action,还可以接受function类型的action。这个function可以带两个参数dispatch为getState,分别指向store的dispatch()方法和getState()方法,然后我们就可以在function中进行做相应异步或条件处理后再调用dispatch()对store发起调度了。

一图看懂(by newtwg):项目结构及代码关键点

2.import引用

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers' //或写全 './reducers/index.js'

3.创建store:

const store = createStore(reducer, applyMiddleware(thunk));

4.在actions.js中导出function类型的action:

export const actioin1 = text=>{
    return function(dispatch,getState){
        setTimeout(()=>{
            //console.log(getState());
            dispatch({type:types.ADD_TODO, text});
        },2000);
    }
}

5.高阶组件传dispatch到原组件props中

Import { action1 } from '../actions'
export default connect(null, { action1 })(Header);

import * as TodoActions from '../actions'
const mapDispatchToProps = dispatch => ({
    actions:bindActionCreators(TodoActions,dispatch)
})
export default connect(null, mapDispatchToProps)(BaseComponent)

这样就不用在connect之前再作处理

6.在原组件中发起调度的方法

const BaseComponent = ({ actions }) => (
    ...
    actions.actioin1 (text)
    ...
)

7.要点小结:

(1)Action写在单独文件中,多了的话也可分组成多个文件,要用哪个引哪个

(2)如果不是特列需求,尽量考虑写在action中进行异步处理

(3)代码要点

①入口js

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const store = createStore(reducer, applyMiddleware(thunk));

②actions.js

export const action1 = text=>{return function(dispatch,getState){/*作异步处理等*/};};

③React-Redux高阶封装

Import { action1 } from '../actions'
export default connect(null, { action1 })(Header)

④原组件

const BaseComponent = ({ actions }) => ( ... actioin1 (text); ... )

  

     

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

New小青龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值