使用 antd, react, redux,creat-react-app搭建todo-list升级版本
redux简介
- redux是一个配合react视图层框架使用的数据层框架
- 方便大型react项目之中的复杂组件传值
- 耦合性高的数据使用redux管理
- redux中包含 组件,store,reducer
-
- store必须是唯一的,整个项目之中只能有一个数据存储空间
-
- store中的数据是store自己更新的,并不是reducer,这也是为什么reducer中不能直接改变state中的数据
-
- Reducer必须是个纯函数。
-
- 纯函数: 是给定固定的输入,就一定会有固定的输出。而且不能有任何的副作用(不能对参数进行修改)
redux数据流向
- store就像一个图书管理员
- 图书管理员会给每个需要借书的人发一个通讯工具(store)
- 通讯工具store有一个方法叫做subscribe(),每当图书馆的图书有变化,这个方法就会自动执行
- 通讯工具store提供一个getState()方法,方便借书人立马得到最新的图书馆数据,配合subscribe()使用
- 通讯工具store提供一个dispatch()方法,方便借书人传达他想借阅的书籍名称
- reducer是图书管理员的查询手册
- 他是图书管理员的查询手册,当图书管理员接到借书人的消息后,他会查阅reducer
- 图书馆管理员也是通过查询手册确定数据的更新
- 查询手册返回的是一个方法,这个方法有2个参数(state,action)
- state就是图书馆数据,action是借书人通过store传递过来的参数,也就是书名,通过action,查询手册才能查询到数据
- reducer返回的方法不能直接更改state
- 组件就像借书人
- 借书人需要借书,通过图书管理员提供的通讯工具store提供的dispatch方法,传达他要借的书(action)
- 借书人通过图书管理员提供的通讯工具store提供的subscribe()和getState()获取图书管的最新咨询
创建项目
- create-react-app todo-list
- 注意项目名称不能有大写字母
删除不必要文件
- src目录中的:App.css, App.test.js, logo.svg, serviceWorker.js文件
- public目录中的: manifest.json文件
安装依赖
- yarn add antd
- yarn add redux
入口index.js编写
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App'; // 引入万年老二组件
ReactDOM.render(<App />, document.getElementById('root'));
创建redux的store
/**
* store就像一个图书管理员,在接到组件(借书人)派发的 dispatch(借书人说的话) 时,
* 他本身不知道书在什么位置,有没有这本书,需要查询 reducer (图书列表)
*/
import { createStore } from 'redux'
import todoListReducer from './reducer' // 引入图书列表
const store = createStore(todoListReducer) // 查询图书列表
export default store
/**
* reducer 相当于图书管理员 store 的查询手册,
* 通过查询手册,确认组件 借书人人需要的书在什么地方。
*/
const todoState = {
inputValue : "",
list: []
}
export default (state=todoState, action) => {
if ( action.type === 'change_input_value'){ // 确认书名, 执行动作
const newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
console.log(newState)
return newState
}
if ( action.type === 'change_list_value'){ // 确认书名, 执行动作
const newState = JSON.parse(JSON.stringify(state))
newState.list = [...state.list, action.item]
newState.inputValue = ''
console.log(newState.list)
return newState
}
return state
}
编写html结构
- 在App.js中引入所需依赖
-
- antd的样式
-
- antd的组件
-
- react
-
- store
/**
* 组件就是一个需要借书的人,通过 dispatch 传达 action (书名)给图书管理员(store)
*/
/**
* 组件就是一个需要借书的人,通过 dispatch 传达 action (书名)给图书管理员(store)
*/
/**
* 组件就是一个需要借书的人,通过 dispatch 传达 action (书名)给图书管理员(store)
*/
import React, { Component, Fragment }from 'react';
import { Input, Button, List, message } from "antd";
import store from './store'; // 引入图书管理员 store
import "antd/dist/antd.css";
class App extends Component {
constructor(props){
super(props)
this.state = store.getState()
console.log(store.getState())
this.handleInputChange = this.handleInputChange.bind(this);
this.addTodoList = this.addTodoList.bind(this);
this.handleStroeChange = this.handleStroeChange.bind(this);
// this.deletTodoList = this.deletTodoList.bind(this);
store.subscribe(this.handleStroeChange) // 图书管理员会随时通知各个借书人,图书馆书籍的变化
}
render() {
return (
<Fragment>
<div style={{ marginTop: '10px', marginLeft: '10px'}}>
<Input
placeholder='todo-list'
style={{ '300px', marginRight: '10px'}}
onChange = { this.handleInputChange }
value = { this.state.inputValue }
/>
<Button
type="primary"
onClick = { this.addTodoList }
>提交</Button>
</div>
<List
style={{ '300px', marginLeft: '10px', marginTop: '5px'}}
size="large"
bordered
dataSource={ this.state.list ? this.state.list : null }
renderItem={ (item, index) => <List.Item style={{position:'relative'}}>
{item}
<Button
type='danger'
style={{position: 'absolute', right: '10px', top:'50%', marginTop:'-5%'}}
onClick={ this.deletTodoList.bind(this, index) }
>删除</Button>
</List.Item>}
/>
</Fragment>
);
}
handleInputChange(e) {
const action = {
type: 'change_input_value', // 借什么书
value: e.target.value
}
store.dispatch(action); // 传达给store
console.log(e.target.value)
}
addTodoList() {
if (this.state.inputValue) {
const action = {
type: 'change_list_value',
item: this.state.inputValue
}
store.dispatch(action)
} else {
message.warning('请输入内容');
}
}
deletTodoList(index) {
const action = {
type: 'delet_list_value',
value: index
}
store.dispatch(action)
}
handleStroeChange() {
this.setState(store.getState()) // 每当图书馆有变化的时候,图书管理员(store)通过这个方式告诉借书人(组件)
}
}
export default App;
ActionType的拆分
- 我们在组件中创建action的时候,配置type等于一个字符串,在reducer中判断action.type的时候,容易出错,并且出错也不会报错。
- 所以我们需要将ActionType做一个拆分
- 在store中新建一个actionTypes.js文件
export const CHANGE_INPUT_VALUE = 'change_input_value'
export const CHANGE_LIST_VALUE = 'change_list_value'
export const DELETE_LIST_VALUE = 'delet_list_value'
- 在需要使用的组件中,以及reducer中导入替换到原来的字符串
/**
* reducer 相当于图书管理员 store 的查询手册,
* 通过查询手册,确认组件 借书人人需要的书在什么地方。
*/
import { CHANGE_INPUT_VALUE, CHANGE_LIST_VALUE, DELETE_LIST_VALUE } from './actionTypes'
const todoState = {
inputValue : "",
list: []
}
export default (state=todoState, action) => {
if ( action.type === CHANGE_INPUT_VALUE){ // 确认书名, 执行动作
const newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
console.log(newState)
return newState
}
if ( action.type === CHANGE_LIST_VALUE){ // 确认书名, 执行动作
const newState = JSON.parse(JSON.stringify(state))
newState.list = [...state.list, action.item]
newState.inputValue = ''
console.log(newState.list)
return newState
}
if ( action.type === DELETE_LIST_VALUE){
const newState = JSON.parse(JSON.stringify(state))
console.log(action.value)
newState.list.splice(action.value, 1)
return newState
}
return state
}
// --------------------------------分割线---------------------------------
/**
* 组件就是一个需要借书的人,通过 dispatch 传达 action (书名)给图书管理员(store)
*/
import React, { Component, Fragment }from 'react';
import { Input, Button, List, message } from "antd";
import store from './store'; // 引入图书管理员 store
import { CHANGE_INPUT_VALUE, CHANGE_LIST_VALUE, DELETE_LIST_VALUE } from './store/actionTypes'
import "antd/dist/antd.css";
class App extends Component {
constructor(props){
super(props)
this.state = store.getState()
console.log(store.getState())
this.handleInputChange = this.handleInputChange.bind(this);
this.addTodoList = this.addTodoList.bind(this);
this.handleStroeChange = this.handleStroeChange.bind(this);
// this.deletTodoList = this.deletTodoList.bind(this);
store.subscribe(this.handleStroeChange) // 图书管理员会随时通知各个借书人,图书馆书籍的变化
}
render() {
return (
<Fragment>
<div style={{ marginTop: '10px', marginLeft: '10px'}}>
<Input
placeholder='todo-list'
style={{ '300px', marginRight: '10px'}}
onChange = { this.handleInputChange }
value = { this.state.inputValue }
/>
<Button
type="primary"
onClick = { this.addTodoList }
>提交</Button>
</div>
<List
style={{ '300px', marginLeft: '10px', marginTop: '5px'}}
size="large"
bordered
dataSource={ this.state.list ? this.state.list : null }
renderItem={ (item, index) => <List.Item style={{position:'relative'}}>
{item}
<Button
type='danger'
style={{position: 'absolute', right: '10px', top:'50%', marginTop:'-5%'}}
onClick={ this.deletTodoList.bind(this, index) }
>删除</Button>
</List.Item>}
/>
</Fragment>
);
}
handleInputChange(e) {
const action = {
type: CHANGE_INPUT_VALUE, // 借什么书
value: e.target.value
}
store.dispatch(action); // 传达给store
console.log(e.target.value)
}
addTodoList() {
if (this.state.inputValue) {
const action = {
type: CHANGE_LIST_VALUE,
item: this.state.inputValue
}
store.dispatch(action)
} else {
message.warning('请输入内容');
}
}
deletTodoList(index) {
const action = {
type: DELETE_LIST_VALUE,
value: index
}
store.dispatch(action)
}
handleStroeChange() {
this.setState(store.getState()) // 每当图书馆有变化的时候,图书管理员(store)通过这个方式告诉借书人(组件)
}
}
export default App;
使用actionCreators统一创建action
- 之前我们创建的action,都是在组件中创建的,但是如果是大型的,逻辑复杂的项目这样写不方便前端测试,也不利于维护
- 因此,我们需要将action统一在一个地方创建
- 在store文件夹中创建一个actionCreators.js,专门用来创建action
- 并且,要在actionCreators中引入我们之前actionTypes.js。
- 然后在需要使用action的组件按需求引入即可
/**
* 其实就是返回一个能获取action的方法
* actionCreators.js
*/
import { CHANGE_INPUT_VALUE, CHANGE_LIST_VALUE, DELETE_LIST_VALUE } from './actionTypes'
export const getInputChangeValue = (value) => ({
type: CHANGE_INPUT_VALUE,
value
})
export const getAddTodoListValue = (item) => ({
type: CHANGE_LIST_VALUE,
item
})
export const getDeletTodoListValue = (index) => ({
type: DELETE_LIST_VALUE,
index
})
// -----------------分割线--------------------------
/**
* App.js
* 在组件中引用action
* 此处省略了无关代码,可以参照上面的代码
*/
/**
* 组件就是一个需要借书的人,通过 dispatch 传达 action (书名)给图书管理员(store)
*/
import React, { Component, Fragment }from 'react';
import { Input, Button, List, message } from "antd";
import store from './store'; // 引入图书管理员 store
// 引入action
import { getInputChangeValue, getAddTodoListValue, getDeletTodoListValue } from './store/actionCreators'
// import { CHANGE_INPUT_VALUE, CHANGE_LIST_VALUE, DELETE_LIST_VALUE } from './store/actionTypes'
import "antd/dist/antd.css";
class App extends Component {
constructor(props){
super(props)
this.state = store.getState()
console.log(store.getState())
this.handleInputChange = this.handleInputChange.bind(this);
this.addTodoList = this.addTodoList.bind(this);
this.handleStroeChange = this.handleStroeChange.bind(this);
// this.deletTodoList = this.deletTodoList.bind(this);
store.subscribe(this.handleStroeChange) // 图书管理员会随时通知各个借书人,图书馆书籍的变化
}
render() {
return (
<Fragment>
<div style={{ marginTop: '10px', marginLeft: '10px'}}>
<Input
placeholder='todo-list'
style={{ '300px', marginRight: '10px'}}
onChange = { this.handleInputChange }
value = { this.state.inputValue }
/>
<Button
type="primary"
onClick = { this.addTodoList }
>提交</Button>
</div>
<List
style={{ '300px', marginLeft: '10px', marginTop: '5px'}}
size="large"
bordered
dataSource={ this.state.list ? this.state.list : null }
renderItem={ (item, index) => <List.Item style={{position:'relative'}}>
{item}
<Button
type='danger'
style={{position: 'absolute', right: '10px', top:'50%', marginTop:'-5%'}}
onClick={ this.deletTodoList.bind(this, index) }
>删除</Button>
</List.Item>}
/>
</Fragment>
);
}
handleInputChange(e) {
/*
const action = {
type: CHANGE_INPUT_VALUE, // 借什么书
value: e.target.value
}
*/
const action = getInputChangeValue(e.target.value)
store.dispatch(action); // 传达给store
console.log(e.target.value)
}
// 添加
addTodoList() {
/*
if (this.state.inputValue) {
const action = {
type: CHANGE_LIST_VALUE,
item: this.state.inputValue
}
store.dispatch(action)
} else {
message.warning('请输入内容');
}
*/
if (this.state.inputValue) {
const action = getAddTodoListValue(this.state.inputValue)
store.dispatch(action)
} else {
message.warning('请输入内容');
}
}
// 删除
deletTodoList(index) {
/*
const action = {
type: DELETE_LIST_VALUE,
value: index
}
*/
const action = getDeletTodoListValue(index)
store.dispatch(action)
}
handleStroeChange() {
this.setState(store.getState()) // 每当图书馆有变化的时候,图书管理员(store)通过这个方式告诉借书人(组件)
}
}
export default App;