public
src
store redux仓库目录
actionCreators.js action目录
actionTypes.js action type目录
index.js redux入口文件,生成store
reducer.js redux逻辑文件,编写各个action所对应的逻辑
index.js react项目入口文件
TodoList.js 组件
package.json
README.md
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import TodoList from './TodoList'
import store from './store'
ReactDOM.render(
,
document.getElementById('root')
);
index.js作为整个react项目的入口文件,负责将组件挂载到#root上。挂载过程须引入React, ReactDOM。
render函数传入两个参数,第一个为需要被挂载的组件,第二个为挂载位置。
挂载组件中Provider包括的组件共享同一个store,由store属性指定。其中Provider和store需要引入。
被挂载的组件放置在Provider中,如TodoList。需引入。
得益于react-redux,TodoList组件大致分为两部分,即逻辑部分与UI部分。通过connect函数连接在一起。
逻辑部分
import { buttonClickAction, inputChangeAction, itemClickAction } from './store/actionCreators';
const stateToProps = (state) => {
return {
inputValue: state.inputValue,
list: state.valueList
}
}
const dispatchToProps = (dispatch) => {
return {
handleInputChange(e){
const value = e.target.value
const action = inputChangeAction(value)
dispatch(action)
},
handleButtonClick(){
const action = buttonClickAction()
dispatch(action)
},
handleLiClick(index){
const action = itemClickAction(index)
dispatch(action)
}
}
}
逻辑部分为两个函数,定义属性的stateToProps
和定义方法的dispatchToProps
。
stateToProps
接收一个参数state,返回一个props对象。其即为UI部分接收到的props属性。
这样做目前能发现的一个好处还有不需要再进行store的subscribe操作进行订阅更新。
dispatchToProps
接收一个参数dispatch,返回一个对象,其中包括所有方法。
在每个方法中,会先通过定义在actionCreators中的函数生成action,随后将action传入dispatch中。dispatch会将action传至reducer中进行处理。
UI部分
import React from 'react';
import {connect} from 'react-redux'
const TodoList = (props) => {
const {inputValue, list, handleInputChange, handleButtonClick, handleLiClick} = props
return (
{list.map((item, index)=> (
- handleLiClick(index)}>{item}
))}
)
}
export default connect(stateToProps, dispatchToProps)(TodoList)
UI部分可以简写成无状态组件,即一个函数。函数接收的props即我们在stateToProps与dispatchToProps中返回对象的合并。我们可以先通过解构赋值来简便我们的编写操作。随后即可直接调用props中的方法。
使用connect函数将两个toProps函数与UI组件结合即可实现react-redux的功能。
store目录为redux的工作目录,与是否安装react-redux插件无关。
actionTypes.js
export const INPUT_CHANGE = 'inputChange'
export const BUTTON_CLICK = 'buttonClick'
export const ITEM_CLICK = 'itemClick'
一般来讲我们需要把所有用到的action type放到一个文件中,并为每一个type定义一个常量。这样做的好处一个是方便集中管理,再一个是可以避免编写错误导致的不显示在控制台的错误。
actionCreators.js
import { BUTTON_CLICK, INPUT_CHANGE, ITEM_CLICK } from "./actionTypes"
export const inputChangeAction = (value) => ({
type: INPUT_CHANGE,
value
})
export const buttonClickAction = () => ({
type: BUTTON_CLICK
})
export const itemClickAction = (index) => ({
type: ITEM_CLICK,
index
})
其次,我们需要将用于生成action的函数放到一个文件中。在不使用中间件的情况下,action必须为一个对象,即这个文件中的每一个函数都要返回一个对象。对象的type即我们在actionTypes中定义的常量。对象的其他属性取决于我们的需要。
index.js
import {createStore} from 'redux'
import reducer from './reducer'
const store = createStore(
reducer
)
export default store
即redux的入口文件。引入一个createStore和我们书写的reducer。暴露出一个createStore生成的对象。其第一个参数为reducer,后面可以添加中间件如redux-thunk或redux-saga。有关中间件的知识我会放到下次进行总结。
reducer.js
import { BUTTON_CLICK, INPUT_CHANGE, ITEM_CLICK } from "./actionTypes"
const defaultState = {
inputValue: 'writting something',
valueList: ['1','2']
}
const reducer = (state=defaultState, action) => {
switch (action.type){
case INPUT_CHANGE:
let newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
case BUTTON_CLICK:
console.log(BUTTON_CLICK)
return state
case ITEM_CLICK:
console.log(action.index)
return state
default:
return state
}
}
export default reducer
即redux的逻辑部分,包含两部分,state的默认值和reducer函数。
其中reudcer函数接收两个参数,第一个为当前的state值,第二个为dispatch的action。函数会通过switch根据action.type的不同采取不同的逻辑。
需要注意的有两点,其一是reducer必须为纯函数,即禁止在其中通过axios等获取其他值,要保证返回的数据与传入的数据相对应,即如果两次传入的数据相同,则两次返回的结果也应该相同。其二为在处理逻辑时不要更改原来的state的值,可以通过深拷贝来生成一个newState变量来进行操作。
如果action.type没有被匹配到,则会返回传入的state值。
一直听说react有一个很难理解的redux。让我在学习之前充满了忐忑。感谢技术胖,可以说是让我毫不费力的入门了redux。传闻中中redux的好处在于避免了各层组件中state的层层传递,不过其实因为项目比较小的缘故还并没有搞懂其中具体的好处。
但react-redux插件确实让我感受到了不少的便利。逻辑与UI的分离让项目的耦合性更低,一方面使得项目的结构更加1鲜明,另一方面也增加了项目更改时的效率。
其实本次学习中感觉收获最大的是对于项目结构的理解。从一开始觉得把type和actioncreator放在单独的文件中然后来回导入导出的麻烦,到后来的的确确发现这样微小的麻烦可以避免更多的大麻烦。可能这也是让我感觉react-redux非常便利的原因之一吧。