P12:Redux进阶-将组件UI和业务逻辑进行拆分

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

P12:Redux进阶-将组件UI和业务逻辑进行拆分

项目目录

/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;
上一篇:第12章 I_O 目录


下一篇:Windows下面安装和配置Solr 4.9(三)支持中文分词器