React学习笔记 在todolist中加入redux

在之前的学习中,我已经使用React完成了todolist,这里我要在原来的基础上加上redux
原来的todolist:React入门 制作简单的todolist
首先,通过npm 安装redux

npm i redux --save

在原来的目录基础上,添加store文件夹,放入index.js来输出store,reducer.js来输出reducer
在这里插入图片描述
在index.js中创建并输出store

import {createStore} from 'redux'
import reducer from './reducer'
const store=createStore(
    reducer,
    // 让redux-devtools得以运行
    window.__REDUX_DEVTOOLS_EXTENSION__&&window.__REDUX_DEVTOOLS_EXTENSION__()    
);
export default store

在reducer中写入默认数据并输出修改state的纯函数

// 默认数据
const defaultState={
    inputValue:'writing something',
    contentList:[{
        id:0,
        title:'todo 1',
        completed:false
    },{
        id:1,
        title:'todo 2',
        completed:false
    }],
    count:2,
    searchWord:""
}

export default (state = defaultState,action)=>{
    // 当前还没有state,所以现在先不做任何处理直接返回state
    return state
}

因为现在我们的数据来源都是store仓库,所以把App.js中的数据和对应修改数据的方法都删除

import React from 'react';
import Header from './component/todo/header';
import Add from './component/todo/add';
import List from './component/todo/list';
import './assets/style/whole.css'

class App extends React.Component{
    render(){
        return(
            <div className="app">
                <Header />
                <Add />
                <List />
            </div>
        )
    }
}

export default App;

接下来,就是要通过发起action来修改store仓库中的state,以list.js其中一个为例
引入store

// 导入store下的index.js时可以忽略index.js不写
import store from '../../store'

在constructor中引入state数据到当前组件的state,绑定一个删除方法

constructor(props){
    super(props);
    this.state=store.getState()
    this.deleteHandle=this.deleteHandle.bind(this);
    // 订阅
    store.subscribe(this.storeChange)
}

注意这里要订阅store中state的变化,否则UI不会跟随变化
然后编写删除方法

deleteHandle(id){
    let list=store.getState().contentList;
    for(let i=0;i<list.length;i++){
        if(list[i].id===id){
            list.splice(i,1);
        }
    }
    let action={
        type:'delete',
        value:list
    }
    // 发起action
    store.dispatch(action)
}

最后在render中调用相应的方法

<button onClick={this.deleteHandle.bind(this,c.id)}>删除</button>

然后回到reducer.js中,在纯函数中对type进行判断并返回新的state

export default (state = defaultState,action)=>{
    console.log('state: ', state);
    if(action.type==='delete'){
        let newState=JSON.parse(JSON.stringify(state));
        newState.contentList=action.value;
        return newState;
    }
    return state
}

到这里删除功能完成,接下来就直接把其他代码补齐了
add.js

import React from 'react';
import '../../assets/style/whole.css'
import store from '../../store'
class Add extends React.Component{
    constructor(props){
        super(props);
        this.state={
            ...store.getState()
        }
        // 为各个方法绑定this
        this.handleChange=this.handleChange.bind(this);
        this.addContent=this.addContent.bind(this);
        // 订阅state
        store.subscribe(()=>{
            this.setState(store.getState())
        })
    }
    // 将输入的内容绑定在state的title上
    handleChange(e){
        let action={
            type:"changeTitle",
            value:e.target.value
        }
        store.dispatch(action)
    }
    // 添加内容
    addContent(){
        if(this.state.searchWord!==''){
            let content={
                title:this.state.searchWord,
                completed:false,
                id:store.getState().count+1
            }
            // 重置输入框内容
            let action={
                type:'addContent',
                value:content
            }
            store.dispatch(action)
            action={
                type:'changeTitle',
                value:''
            }
            store.dispatch(action)
        }
    }
    render(){
        return(    
            <div className="addContent">
                <input placeholder={this.state.inputValue} value={store.getState().searchWord} onChange={this.handleChange}/>
                <button onClick={this.addContent}>add</button>
            </div>
        )}
}

export default Add;

list.js

import React from 'react';
import '../../assets/style/whole.css'
// 导入store下的index.js时可以忽略index.js不写
import store from '../../store'

class List extends React.Component{
    constructor(props){
        super(props);
        this.state=store.getState()
        this.deleteHandle=this.deleteHandle.bind(this);
        this.storeChange=this.storeChange.bind(this);
        this.onCompletedChange=this.onCompletedChange.bind(this);
        // 订阅
        store.subscribe(this.storeChange)
    }
    storeChange(){
        this.setState(store.getState())
    }
    deleteHandle(id){
        let list=store.getState().contentList;
        for(let i=0;i<list.length;i++){
            if(list[i].id===id){
                list.splice(i,1);
            }
        }
        let action={
            type:'delete',
            value:list
        }
        // 发起action
        store.dispatch(action)
    }
    onCompletedChange(id){
        let list=store.getState().contentList;
        for(let i=0;i<list.length;i++){
            if(list[i].id===id){
                list[i].completed=!list[i].completed;
            }
        }
        let action={
            type:'completedChange',
            value:list
        }
        store.dispatch(action)
    }
    render(){  
        // 按列表标题的数量来渲染内容
        return(    
            <div className="contentList">
                <ul>
                    {this.state.contentList.map((c)=>{
                        return(
                            <li className="detailContent" key={c.id}>
                                <input type="checkbox" onChange={this.onCompletedChange.bind(this,c.id)}/>
                                <span className={c.completed?'haveCompleted':'notCompleted'}>{c.title}</span>
                                <button onClick={this.deleteHandle.bind(this,c.id)}>删除</button>
                            </li>
                        )
                    })}
                </ul>
            </div>
        )}
}

export default List;

reducer.js

const defaultState={
    inputValue:'writing something',
    contentList:[{
        id:0,
        title:'todo 1',
        completed:false
    },{
        id:1,
        title:'todo 2',
        completed:false
    }],
    // 计数,每次增加一个新内容时赋值给id然后加1
    count:2,
    searchWord:""
}

export default (state = defaultState,action)=>{
    console.log('state: ', state);
    if(action.type==='addContent'){
        let newState=JSON.parse(JSON.stringify(state));
        newState.contentList[newState.count]=action.value;
        newState.count++;
        newState.searchWord="";
        return newState
    }else if(action.type==='changeTitle'){
        let newState=JSON.parse(JSON.stringify(state));
        newState.searchWord=action.value;
        return newState;
    }else if(action.type==='delete'||action.type==='completedChange'){
        let newState=JSON.parse(JSON.stringify(state));
        newState.contentList=action.value;
        return newState;
    }
    return state
}

到这里就完成了在todolist中加入redux的工作了,但是在这里仍有很多可以改进的地方,action的type可以使用字符串常量,放在一个专门的文件里来管理,action的发起可以封装函数来完成,达到复用的效果
这里只是简单的完成在todolist中加入redux,所以其他的这些优化,就有待后面完成了

发布了178 篇原创文章 · 获赞 12 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/zemprogram/article/details/102268574