1.Ant Design介绍和环境初始化
Ant Design
是一套面向企业级开发的UI框架,视觉和动效作的很好。阿里开源的一套UI框架,它不只支持React
,还有ng
和Vue
的版本,我认为不论你的前端框架用什么,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;
仓库store
和reducer
都创建好了,可以初始化下App.js中的数据了,在reducer.js
文件的defaultState
对象中,加入两个属性:inputValue
和list
。
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中的无状态组件
- 首先我们不在需要引入React中的
{ Component }
,删除就好。 - 然后些一个
TodoListUI
函数,里边只返回JSX
的部分就好,这步可以复制。 - 函数传递一个
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;
}
个人博客:Karma‘s Blog
源码地址:传送门