在之前的学习中,我已经使用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,所以其他的这些优化,就有待后面完成了