Redux 进阶
阐述
Redux的基础知识都学完了,但是你离高手还差一点东西,就是如何拆分UI部分和业务逻辑,让项目更容易维护,既然能拆分了,就代表能更多人协作,实现超大型项目的开发和快速上线。
拆分UI组件
可以看到 TodoList.js
组件是UI和业务逻辑完全耦合在一起的,这时候在 src
目录下新建一个文件叫 TodoListUI.js
,快速生成页面的基本结构。
import React, { Component } from 'react';
class TodoListUi extends Component {
render() {
return ( <div>123</div> );
}
}
export default TodoListUi;
然后去 TodoList.js
里把JSX的部分拷贝过来, 现在的代码如下(当然现在是不可以使用的,好多东西我们还没有引入,所以会报错):
import React, { Component } from 'react';
class TodoListUi extends Component {
render() {
return (
<div style={{margin:'10px'}}>
<div>
<Input
placeholder={this.state.inputValue}
style={{ width:'250px', marginRight:'10px'}}
onChange={this.changeInputValue}
value={this.state.inputValue}
/>
<Button
type="primary"
onClick={this.clickBtn}
>增加</Button>
</div>
<div style={{margin:'10px',width:'300px'}}>
<List
bordered
dataSource={this.state.list}
renderItem={(item,index)=>(<List.Item onClick={this.deleteItem.bind(this,index)}>{item}</List.Item>)}
/>
</div>
</div>
);
}
}
export default TodoListUi;
要想可用,第一步是需要引入antd
的相关类库,这时候你可以拷贝 TodoList.js
的相关代码,把 antd
的CSS和用到的组件都拷贝过来,进行引入。
import 'antd/dist/antd.css'
import { Input , Button , List } from 'antd'
但是这并没有 TodoListUI.js
组件所需要的 state(状态信息),接下来需要改造父组件进行传递值。
TodoList.js 文件的修改
TodoList.js
里就不需要这么多JSX代码了,只要引入 TodoListUI.js
。
import TodoListUI from './TodoListUI'
引入之后render函数就可以写成下面这个样子。
render() {
return (
<TodoListUI />
);
}
这样就算做完UI和业务分离的第一步了,剩下的就是改变 TodoListUI.js
里边的属性了,也就是两个组件的整合。
UI组件和业务逻辑组件的整合
其实整合就是通过属性传值的形式,把需要的值传递给子组件,子组件接收这些值,进行相应的绑定就可以了。
TodoList.js
的 render 部分
render() {
return (
<TodoListUI
inputValue={this.state.inputValue}
list={this.state.list}
changeInputValue={this.changeInputValue}
clickBtn={this.clickBtn}
deleteItem={this.deleteItem}
/>
);
}
你还需要在 constructor
(构造函数里)对 deleteItem
方法进行重新绑定 this
。
代码如下:
this.deleteItem = this.deleteItem.bind(this)
修改完 TodoList.js
文件,还要对UI组件进行对应的属性替换,所有代码如下。
demo01\src\TodoListUI.js
import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { Input , Button , List } from 'antd'
class TodoListUi extends Component {
render() {
return (
<div style={{margin:'10px'}}>
<div>
<Input
style={{ width:'250px', marginRight:'10px'}}
onChange={this.props.changeInputValue}
value={this.props.inputValue}
/>
<Button
type="primary"
onClick={this.props.clickBtn}
>增加</Button>
</div>
<div style={{margin:'10px',width:'300px'}}>
<List
bordered
dataSource={this.props.list}
renderItem={(item,index)=>(<List.Item onClick={(index)=>{this.props.deleteItem(index)}}>{item}</List.Item>)}
/>
</div>
</div>
);
}
}
export default TodoListUi;
需要注意的是在 List
组件的删除功能,需要用箭头函数的形式,代替以前方法,并在箭头函数里使用属性的方法,调用出你传过来的方法。
<List
bordered
dataSource={this.props.list}
renderItem={(item,index)=>(<List.Item onClick={()=>{this.props.deleteItem(index)}}>{item}</List.Item>)}
/>
这些都做完了,你就已经把组件进行了拆分,本文学习的目的不是拆分的步骤,而是拆分的思想,你可以反复几次来加深对UI和业务逻辑拆分的理解。
demo
项目目录
/ReactDemo
$ ls -al
total 12
drwxr-xr-x 1 Administrator 197121 0 12月 7 11:31 ./
drwxr-xr-x 1 Administrator 197121 0 11月 29 16:21 ../
drwxr-xr-x 1 Administrator 197121 0 12月 7 11:32 demo01/
------------------------------------------------------------------
ReactDemo/demo01 (master)
$ ls -al
total 1921
drwxr-xr-x 1 Administrator 197121 0 12月 7 11:32 ./
drwxr-xr-x 1 Administrator 197121 0 12月 7 11:31 ../
drwxr-xr-x 1 Administrator 197121 0 12月 7 11:32 .git/
-rw-r--r-- 1 Administrator 197121 310 12月 7 11:31 .gitignore
drwxr-xr-x 1 Administrator 197121 0 12月 13 18:31 node_modules/
-rw-r--r-- 1 Administrator 197121 948 12月 13 18:31 package.json
-rw-r--r-- 1 Administrator 197121 1547406 12月 13 18:31 package-lock.json
drwxr-xr-x 1 Administrator 197121 0 12月 7 11:32 public/
-rw-r--r-- 1 Administrator 197121 3369 12月 7 11:31 README.md
drwxr-xr-x 1 Administrator 197121 0 12月 14 17:04 src/
------------------------------------------------------------------
ReactDemo/demo01/src (master)
$ ls -al
total 17
drwxr-xr-x 1 Administrator 197121 0 12月 14 17:04 ./
drwxr-xr-x 1 Administrator 197121 0 12月 7 11:32 ../
-rw-r--r-- 1 Administrator 197121 159 12月 13 18:04 index.js
drwxr-xr-x 1 Administrator 197121 0 12月 14 16:18 store/
-rw-r--r-- 1 Administrator 197121 1519 12月 14 17:31 TodoList.js
-rw-r--r-- 1 Administrator 197121 1123 12月 14 17:23 TodoListUI.js
------------------------------------------------------------------
ReactDemo/demo01/src/store (master)
$ ls -al
total 11
drwxr-xr-x 1 Administrator 197121 0 12月 14 16:18 ./
drwxr-xr-x 1 Administrator 197121 0 12月 14 17:04 ../
-rw-r--r-- 1 Administrator 197121 302 12月 14 16:29 actionCreators.js
-rw-r--r-- 1 Administrator 197121 127 12月 14 16:08 actionTypes.js
-rw-r--r-- 1 Administrator 197121 281 12月 13 19:14 index.js
-rw-r--r-- 1 Administrator 197121 1068 12月 14 16:11 reducer.js
demo01\src\index.js
import React from 'react';
import ReactDOM from 'react-dom'
import TodoList from './TodoList'
ReactDOM.render(<TodoList/>,document.getElementById('root'))
demo01\src\TodoList.js
import React, { Component } from 'react';
import store from './store'
//关键代码-------------start
import {changeInputAction , addItemAction ,deleteItemAction} from './store/actionCreators'
//关键代码------------end
import TodoListUI from './TodoListUI'
class TodoList extends Component {
constructor(props){
super(props)
this.state=store.getState();
this.changeInputValue= this.changeInputValue.bind(this)
this.storeChange = this.storeChange.bind(this)
this.clickBtn = this.clickBtn.bind(this)
store.subscribe(this.storeChange) //订阅Redux的状态
}
render() {
return (
<TodoListUI
inputValue={this.state.inputValue}
list={this.state.list}
changeInputValue={this.changeInputValue}
clickBtn={this.clickBtn}
deleteItem={this.deleteItem}
/>
);
}
storeChange(){
console.log('store changed')
this.setState(store.getState())
}
//--------关键代码------start
changeInputValue(e){
const action = changeInputAction(e.target.value)
store.dispatch(action)
}
clickBtn(){
const action = addItemAction()
store.dispatch(action)
}
deleteItem(index){
const action = deleteItemAction(index)
store.dispatch(action)
}
//--------关键代码------end
}
export default TodoList;
demo01\src\TodoListUI.js
import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { Input , Button , List } from 'antd'
class TodoListUi extends Component {
render() {
return (
<div style={{margin:'10px'}}>
<div>
<Input
style={{ width:'250px', marginRight:'10px'}}
onChange={this.props.changeInputValue}
value={this.props.inputValue}
/>
<Button
type="primary"
onClick={this.props.clickBtn}
>增加</Button>
</div>
<div style={{margin:'10px',width:'300px'}}>
<List
bordered
dataSource={this.props.list}
renderItem={(item,index)=>(<List.Item onClick={()=>{this.props.deleteItem(index)}}>{item}</List.Item>)}
/>
</div>
</div>
);
}
}
export default TodoListUi;
demo01\src\store\index.js
import { createStore } from 'redux' // 引入createStore方法
import reducer from './reducer'
const store = createStore(reducer,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()) // 创建数据存储仓库
export default store //暴露出去
demo01\src\store\reducer.js
import {CHANGE_INPUT,ADD_ITEM,DELETE_ITEM} from './actionTypes'
const defaultState = {
inputValue : 'Write Something',
list:[
'工作再忙也要锻炼身体',
'中午下班休息一小时'
]
}
export default (state = defaultState,action)=>{
if(action.type === CHANGE_INPUT){
let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
newState.inputValue = action.value
return newState
}
//state值只能传递,不能使用
if(action.type === ADD_ITEM ){ //根据type值,编写业务逻辑
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue) //push新的内容到列表中去
newState.inputValue = ''
return newState
}
if(action.type === DELETE_ITEM ){ //根据type值,编写业务逻辑
let newState = JSON.parse(JSON.stringify(state))
newState.list.splice(action.index,1) //push新的内容到列表中去
return newState
}
return state
}
demo01\src\store\actionTypes.js
export const CHANGE_INPUT = 'change_input_value'
export const ADD_ITEM = 'addItem'
export const DELETE_ITEM = 'deleteItem'
demo01\src\store\actionCreators.js
import {CHANGE_INPUT , ADD_ITEM,DELETE_ITEM} from './actionTypes'
export const changeInputAction = (value)=>({
type:CHANGE_INPUT,
value
})
export const addItemAction = ()=>({
type:ADD_ITEM
})
export const deleteItemAction = (index)=>({
type:DELETE_ITEM,
index
})
Assign arrow function to a variable before exporting as module default
在导出为模块默认导入之前,将箭头函数指定给变量。
import {CHANGE_INPUT,ADD_ITEM,DELETE_ITEM} from './actionTypes'
const defaultState = {
inputValue : 'Write Something',
list:[
'工作再忙也要锻炼身体',
'中午下班休息一小时'
]
}
const stateAction = (state = defaultState,action)=>{
if(action.type === CHANGE_INPUT){
let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
newState.inputValue = action.value
return newState
}
//state值只能传递,不能使用
if(action.type === ADD_ITEM ){
//根据type值,编写业务逻辑
let newState = JSON.parse(JSON.stringify(state))
//push新的内容到列表中去
newState.list.push(newState.inputValue)
newState.inputValue = ''
return newState
}
//根据type值,编写业务逻辑
if(action.type === DELETE_ITEM ){
let newState = JSON.parse(JSON.stringify(state))
//push新的内容到列表中去
newState.list.splice(action.index,1)
return newState
}
return state
}
export default stateAction;