使用 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( , 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 (
提交
{item}
删除
}
/>
);
}
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 (
提交
{item}
删除
}
/>
);
}
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 (
提交
{item}
删除
}
/>
);
}
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;