• 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={{ '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;
    
    
    
  • 相关阅读:
    流水账
    还是有希望啊
    The Java Tutorials:Collections
    绘图框架新需求
    Fractal Tree扩展
    js获取字符串最后一个字符代码
    js保留小数点后N位的方法介绍
    JS 实现 ResizeBar,可拖动改变两个区域(带iframe)大小
    js获取浏览器高度和宽度值,尽量的考虑了多浏览器。
    jquery自动将form表单封装成json的具体实现
  • 原文地址:https://www.cnblogs.com/boye-1990/p/11453246.html
Copyright © 2020-2023  润新知