react入门系列之使用 antd, react, redux,creat-react-app搭建todo-list升级版本

使用 antd, react, redux,creat-react-app搭建todo-list升级版本

redux简介

  • redux是一个配合react视图层框架使用的数据层框架
  • 方便大型react项目之中的复杂组件传值
  • 耦合性高的数据使用redux管理
  • redux中包含 组件,store,reducer
    1. store必须是唯一的,整个项目之中只能有一个数据存储空间
    1. store中的数据是store自己更新的,并不是reducer,这也是为什么reducer中不能直接改变state中的数据
    1. Reducer必须是个纯函数。
    1. 纯函数: 是给定固定的输入,就一定会有固定的输出。而且不能有任何的副作用(不能对参数进行修改)

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

  • index.js
/**
 * store就像一个图书管理员,在接到组件(借书人)派发的 dispatch(借书人说的话) 时,
 * 他本身不知道书在什么位置,有没有这本书,需要查询 reducer (图书列表)
 */
import { createStore } from 'redux'
import todoListReducer from './reducer' // 引入图书列表

const store = createStore(todoListReducer) // 查询图书列表

export default store
  • reducer.js
/**
 * 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中引入所需依赖
    1. antd的样式
    1. antd的组件
    1. react
    1. 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={{width: '300px', marginRight: '10px'}}
          onChange = { this.handleInputChange }
          value = { this.state.inputValue }
          />
          <Button 
          type="primary"
          onClick = { this.addTodoList }
          >提交</Button>
        </div>
        <List
        style={{width: '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={{width: '300px', marginRight: '10px'}}
          onChange = { this.handleInputChange }
          value = { this.state.inputValue }
          />
          <Button 
          type="primary"
          onClick = { this.addTodoList }
          >提交</Button>
        </div>
        <List
        style={{width: '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={{width: '300px', marginRight: '10px'}}
          onChange = { this.handleInputChange }
          value = { this.state.inputValue }
          />
          <Button 
          type="primary"
          onClick = { this.addTodoList }
          >提交</Button>
        </div>
        <List
        style={{width: '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;


posted @ 2019-09-03 15:20  Byhua  阅读(382)  评论(0编辑  收藏  举报