1.Ant Design介绍和环境初始化

Ant Design是一套面向企业级开发的UI框架,视觉和动效作的很好。阿里开源的一套UI框架,它不只支持React,还有ngVue的版本,我认为不论你的前端框架用什么,Ant Design都是一个不错的选择。习惯性把AntDesign简称为antd

官网地址:https://ant.design/index-cn

2.项目初始化

#安装脚手架工具
cnpm install -g create-react-app
create-react-app demo05-redux
cd demo05-redux
yarn start

3.生成基本代码结构

删除src下所有文件,创建index.js。

import React from "react";
import ReactDOM from 'react-dom'
import App from './App'

ReactDOM.render(<App/>,document.getElementById('root'))

创建App.js。

import React, {Component} from "react";

class App extends Component {
    render() {
        return (
            <div>Hello World</div>
        )
    }
}

export default App;

3.安装Ant Design

cnpm install antd --save
#
yarn add antd

4.使用Ant Design制作基础UI界面

引入CSS样式

import 'antd/dist/antd.css'

编写Input框和Button按钮

import React, {Component} from "react";
import 'antd/dist/antd.css'
import {Input,Button} from "antd";//需要什么组件,引入什么组件

class App extends Component {
    render() {
        return (
            <div>
                <div>
                    <Input placeholder={'karma'} style={{ width:'250px'}}/>
                    <Button type="primary">添加</Button>
                </div>
            </div>
        )
    }
}

export default App;

添加List列表,首先我们需要在class外声明一个data数组

const data=[
    'test1',
    'test2',
    'test3'
]

再在App.js中,引入List组件。

import {Input,Button,List} from "antd";//需要什么组件,引入什么组件

添加List组件。

import React, {Component} from "react";
import 'antd/dist/antd.css'
import {Input, Button, List} from "antd";//需要什么,引入什么

const data = [
    'test1',
    'test2',
    'test3'
]

class App extends Component {
    render() {
        return (
            <div>
                <div>
                    <Input placeholder={'karma'} style={{width: '250px'}}/>
                    <Button type="primary">添加</Button>
                </div>
                {/*主要代码*/}
                <div>
                    <List
                        bordered
                        dataSource={data}
                        renderItem={item => (<List.Item>{item}</List.Item>)}
                    ></List>
                </div>
                {/*主要代码*/}
            </div>
        )
    }
}

export default App;

5.创建Redux中的仓库-store和reducer

首先需要先安装Redux。

cnpm install --save redux

再在src目录下创建store目录,在store目录下创建index.js。

import { createStore } from 'redux'  // 引入createStore方法
const store = createStore()          // 创建数据存储仓库
export default store                 //暴露出去

再创建reducer.js在store目录下。

const defaultState = {}  //默认数据
export default (state = defaultState,action)=>{  //就是一个方法函数
  //state: 是整个项目中需要管理的数据信息,这里我们没有什么数据,所以用默认空的来表示。
    return state
}

最后在store/index.js中引入reducer,再将reducer以参数的形式传递给createStore方法。

import {createStore} from "redux";
import reducer from "./reducer";
const store =createStore(reducer);
export default store;

仓库storereducer都创建好了,可以初始化下App.js中的数据了,在reducer.js文件的defaultState对象中,加入两个属性:inputValuelist

const defaultState = {
    inputValue: 'karma',
    list: [
        'test1',
        'test2',
        'test3'
    ]
}  //默认数据

让App.js组件获取到store中初始化的数据,在组件引入store。

import store from './store'
#或者
import store from './store/index'

再在组件的构造函数中获取到数据,将它赋值给组件的state,最后再将数据进行渲染。

import React, {Component} from "react";
import 'antd/dist/antd.css'
import {Input, Button, List} from "antd";//需要什么,引入什么
import store from "./store";

class App extends Component {
    // 主要代码
    constructor(props) {
        super(props);
        this.state=store.getState();
    }
    // 主要代码
    render() {
        return (
            <div>
                <div>
                    <Input placeholder={'karma'} style={{width: '250px'}}/>
                    <Button type="primary">添加</Button>
                </div>
                <div>
                    {/*主要代码*/}
                    <List
                        bordered
                        dataSource={this.state.list}
                        renderItem={item => (<List.Item>{item}</List.Item>)}
                    ></List>
                    {/*主要代码*/}
                </div>
            </div>
        )
    }
}
export default App;

6.通过Input体验Redux的流程

给Input添加onChange时间,并在构造函数中进行bind绑定。

##创建inputChange方法
changeInputValue(e){
    console.log(e.target.value)
}

##Input添加onChange事件,并再添加一个Input框,用来获取到第一个Input值的变化情况
<Input placeholder={'karma'} style={{width: '250px'}} onChange={this.inputChange}/>
<Input value={this.state.inputValue} style={{width: '250px'}} onChange={this.inputChange}/>
##构造函数中,进行绑定
this.inputChange=this.inputChange.bind(this)

在changeInputValue方法中,创建Action交互动作。

inputChange(e) {
  const action = {
    type: 'inputChange',
    ##获取到Input框的值
    value: e.target.value
  }
  ##通过dispatch传递到store中
  store.dispatch(action)
}

然后在reducer.js中进行数据改变操作。

export default (state = defaultState, action) => {  //就是一个方法函数
    //state: 是整个项目中需要管理的数据信息,这里我们没有什么数据,所以用默认的来表示。
    if (action.type==='inputChange'){
      	//Reducer里只能接收state,不能改变state,所以我们进行深度拷贝
        let newState=JSON.parse(JSON.stringify(state))//深度拷贝
        newState.inputValue=action.value
        return newState
    }
    return state
}

记住:Reducer里只能接收state,不能改变state。

接着我们需要使得组件发生更新,我们需要在App组件构造函数中添加store订阅。

    
constructor(props) {
  super(props);
  this.state = store.getState();
  this.inputChange = this.inputChange.bind(this)
  // 主要代码
  this.storeChange = this.storeChange.bind(this)
  store.subscribe(this.storeChange)
  // 主要代码
}

##并添加storeChange方法,进行重新setState更新组件数据
storeChange() {
  this.setState(store.getState())
}

然后我们在浏览器第一个Input框中输入值,第二个Input也随着变化。

7.添加Button响应事件

首先我们给Button添加onClick点击事件,并在构造函数中进行绑定。

<Input value={this.state.inputValue} style={{width: '250px'}} onChange={this.inputChange}/>
<Button type="primary" onClick={this.addItem}>添加</Button>

##构造函数中添加绑定
this.addItem = this.addItem.bind(this)

##添加addItem方法,创建action,并通过dispatch将操作传递给store
addItem() {
  const action = {
    type: 'addItem'
  }
  store.dispatch(action)
}

然后我们在reducer.js进行list数据添加操作。

export default (state = defaultState, action) => {  //就是一个方法函数
    //state: 是整个项目中需要管理的数据信息,这里我们没有什么数据,所以用默认的来表示。
    if (action.type === 'inputChange') {
        let newState = JSON.parse(JSON.stringify(state))//深度拷贝
        newState.inputValue = action.value
        return newState
    }
    if (action.type === 'addItem') {
        let newState = JSON.parse(JSON.stringify(state))//深度拷贝
        newState.list.push(newState.inputValue)
        newState.inputValue = ''
        return newState
    }
    return state
}

最后我们就可以点击Button按钮进行List数据添加。

8.通过Redux进行List的删除

首先,我们需要给List子项绑定onClick事件,并传递下标进行绑定。

<List
    bordered
    dataSource={this.state.list}
    renderItem={(item, index) => (
               <List.Item onClick={this.itemChange.bind(this, index)}>{item}</List.Item>)}></List>
      
##添加删除方法,将action操作通过dispatch传递到store中
    deleteItem(index) {
      const action = {
        type: 'deleteItem',
        index
      }
      store.dispatch(action)
    }

再在reducer.js中,进行数据删除操作更新。

if (action.type === 'deleteItem') {
  let newState = JSON.parse(JSON.stringify(state))//深度拷贝
  newState.list.splice(action.index, 1)
  return newState
}

然后在浏览器中,我们就可以点击item子项,进行List数据删除。

9.把Action Type单独创建一个js文件管理

#创建store/actionTypes.js
export const CHANGE_INPUT='changeInput'
export const ADD_INFO='addInfo'
export const DELETE_ITEM='deleteItem'
export const GET_LIST='getList'

引入Action中使用

#actionTypes.js
import {CHANGE_INPUT, ADD_INFO, DELETE_ITEM} from './store/actionTypes'

changeInput(e) {
  const action = {
    type: CHANGE_INPUT,
    value: e.target.value
  }
  store.dispatch(action)
}

clickBtn() {
  const action = {
    type: ADD_INFO
  }
  store.dispatch(action)
}

deleteItem(index) {
  const action = {
    type: DELETE_ITEM,
    index
  }
  store.dispatch(action)
}

引入Reducer并进行更改

#reducer.js
import {CHANGE_INPUT, ADD_INFO, DELETE_ITEM} from './actionTypes'

if (action.type === CHANGE_INPUT) {
  let newState = JSON.parse(JSON.stringify(state))//深度拷贝state
  newState.inputValue = action.value
  return newState
}
if (action.type === ADD_INFO) {
  let newState = JSON.parse(JSON.stringify(state))//深度拷贝state
  newState.list.push(newState.inputValue)
  newState.inputValue = ''
  return newState
}
if (action.type === DELETE_ITEM) {
  let newState = JSON.parse(JSON.stringify(state))//深度拷贝state
  newState.list.splice(action.index, 1)
  return newState;
}

10.编写actionCreators.js

为了让action方便统一管理,使得代码更加简洁。

#actionCreators.js
import {CHANGE_INPUT, ADD_INFO, DELETE_ITEM, GET_LIST} from './actionTypes'

export const changeInput = (value) => ({
    type: CHANGE_INPUT,
    value
})

export const clickBtn = () => ({
    type: ADD_INFO
})

export const deleteItem = (index) => ({
    type: DELETE_ITEM,
    index
})

在App.js中引入action常量方法

#App.js
import {changeInput, clickBtn, deleteItem} from './store/actionCreators'

#方法修改
changeInput(e) {
  store.dispatch(changeInput(e.target.value))
}

clickBtn() {
  store.dispatch(clickBtn())
}

deleteItem(index) {
  store.dispatch(deleteItem(index))
}

11.Redux所遇到的坑

  • store必须是唯一的,多个store是坚决不允许,只能有一个store空间
  • 只有store能改变自己的内容,Reducer不能改变
  • Reducer必须是纯函数

12.组建UI和业务拆分

拆分UI组件,首先我们先创建一个AppUI.js文件,将App.js中的JSX部分代码复制过来,并引入所需的相关组件

import React, {Component} from "react";
import 'antd/dist/antd.css'
import { Input , Button , List } from 'antd'
class AppUI extends Component {
    render() {
        return (
             <div style={{margin: '10px'}}>
                <div>
                    <Input style={{width: '250px', marginRight: '10px'}}
                           onChange={this.changeInput} value={this.state.inputValue}/>
                    <Button type={"primary"} onClick={this.clickBtn}>添加</Button>
                </div>
                <div>
                    <List
                        bordered
                        dataSource={this.state.list}
                        renderItem={(item, index) => (
                            <List.Item onClick={this.deleteItem.bind(this, index)}>{item}</List.Item>
                        )}
                    />
                </div>
            </div>
        )
    }
}

export default AppUI;

修改App.js文件

#引入AppUI组件
import AppUI from "./AppUI";

#并修改JSX部分代码
render() { 
    return ( 
        <AppUI />
    );
}
UI组件和业务逻辑组件整合
#App.js

constructor(props) {
  super(props);
  this.state = store.getState();
  this.changeInput = this.changeInput.bind(this)
  this.clickBtn = this.clickBtn.bind(this)
  #需要在constructor(构造函数里)对deleteItem方法进行重新绑定this
  this.deleteItem = this.deleteItem.bind(this)
  this.storeChange = this.storeChange.bind(this)
  store.subscribe(this.storeChange)
}
render() {
  return (
    <AppUI
    changeInput={this.changeInput}
		inputValue={this.state.inputValue}
		clickBtn={this.clickBtn}
		list={this.state.list}
		deleteItem={this.deleteItem}
/>
  );
}
#AppUI.js
import React, {Component} from "react";
import {Button, Input, List} from "antd";
import 'antd/dist/antd.css'
class AppUI extends Component {
    render() {
        return (
            <div style={{margin: '10px'}}>
                <div>
                    <Input style={{width: '250px', marginRight: '10px'}}
                           onChange={this.props.changeInput} value={this.props.inputValue}/>
                    <Button type={"primary"} onClick={this.props.clickBtn}>添加</Button>
                </div>
                <div>
                    <List
                        bordered
                        dataSource={this.props.list}
                        renderItem={(item, index) => (
                            <List.Item onClick={() => this.props.deleteItem(index)}>{item}</List.Item>
                        )}
                    />
                </div>
            </div>
        )
    }
}

export default AppUI;

需要注意的是在List组件的删除功能,需要用箭头函数的形式,代替以前方法,并在箭头函数里使用属性的方法,调用出啊你过来的方法。

<List
    bordered
    dataSource={this.props.list}
    renderItem={(item,index)=>(<List.Item onClick={()=>{this.props.deleteItem(index)}}>{item}</List.Item>)}
/>

13.Redux中的无状态组件

  1. 首先我们不在需要引入React中的{ Component },删除就好。
  2. 然后些一个TodoListUI函数,里边只返回JSX的部分就好,这步可以复制。
  3. 函数传递一个props参数,之后修改里边的所有props,去掉this
import React from "react";
import {Button, Input, List} from "antd";
import 'antd/dist/antd.css'

const AppUI = (props) => {
    return (
        <div style={{margin: '10px'}}>
            <div>
                <Input style={{width: '250px', marginRight: '10px'}}
                       onChange={props.changeInput} value={props.inputValue}/>
                <Button type={"primary"} onClick={props.clickBtn}>添加</Button>
            </div>
            <div>
                <List
                    bordered
                    dataSource={props.list}
                    renderItem={(item, index) => (
                        <List.Item onClick={() => props.deleteItem(index)}>{item}</List.Item>
                    )}
                />
            </div>
        </div>
    )
}

export default AppUI;

14.Axios异步获取list数据

利用easy-mock模拟数据

https://www.easy-mock.com/mock/5f96cc6134c55d14fda96ea1/example/query

#自定义返回数据格式
{
  "success": true,
  "data": {
    "list": [
      "test1",
      "test2",
      "test3"
    ]
  }
}

安装并使用Axios

cnpm install --save axios

在App.js中引入axios,并在组件中声明周期函数

import axios from 'axios'

#声明周期函数 componentDidMount
componentDidMount(){
    axios.get('https://www.easy-mock.com/mock/5f96cc6134c55d14fda96ea1/example/query').then((res)=>{
        console.log(res)
    })
}

将数据渲染传递给list,首先我们需要在store/actionCreators.js文件中,引入action常量,创建一个新的函数,

#actionTypes.js
export const GET_LIST='getList'

#actionCreators.js
import {GET_LIST} from './actionTypes'
export const getList = (list) => ({
    type: GET_LIST,
    list
})

然后在App.js中引入函数,并将数据通过store.dispatch传递list值

import {getList} from './store/actionCreators'

componentDidMount() {
  Axios.get('https://www.easy-mock.com/mock/5f96cc6134c55d14fda96ea1/example/query').then((res) => {
    console.log(res)
    store.dispatch(getList(res.data.data.list))
  })
}

最后在reducer.js中将值赋给list

#引入action常量
import {GET_LIST} from './actionTypes'

#创建action事件
if (action.type === GET_LIST) {
  let newState = JSON.parse(JSON.stringify(state))//深度拷贝statec
  newState.list=action.list
  return newState;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

13 Redux中间件-thunk和saga 上一篇
11 Redux认识 下一篇