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;


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容