06.Redux入门
Redux概念简述
image.pngRedux的工作流程
image.png使用Antd实现TodoList页面布局
这里,我们新建一个项目
1.cmd命令行运行
creat-react-app reduxdemo
2.根据之前学过的内容,整理项目到最简状态(在01这个文章里)
3.导入antUI
npm install antd --save
4.编写新的TodoList
这里
Input
Button
List
这三给组件可以从AntDesign官网里去找使用方法
//===>src/TodoList.js
import React, { Component } from 'react'
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
const data = [
'Racing car sprays burning fuel into crowd.',
'Japanese princess to wed commoner.',
'Australian walks 100km after outback crash.',
'Man charged over missing wedding girl.',
'Los Angeles battles huge wildfires.',
];
class TodoList extends Component {
render() {
return (
<div style={{ marginTop: '10px', marginLeft: '10px' }}>
<div>
<Input placeholder="todo info" style={{ width: '300px', marginRight: '10px' }} />
<Button type="primary">提交</Button>
</div>
<List
style={{ marginTop: '10px', width: '300px' }}
bordered
dataSource={data}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
}
export default TodoList
5.导入这个组件到项目
//===>src/App.js
import React from 'react';
import TodoList from './TodoList'
function App() {
return (
<div className="App">
<TodoList />
</div>
);
}
export default App;
效果
创建redux中的store
1.我们先创建reducer.js文件,defaultState定义要存储的数据对象,并传给state。
2.接着,我们把存数据的reducer,传给store。后面我们就可以从store里面取数据了
3.在TodoList文件中,通过store.getState()拿到store,就可以使用store的内容了。
1.安装redux
cnpm install --save redux
2.创建reducer.js
//===>src/store/reducer.js
const defaultState = {
inputValue: '123',
list: [1, 2]
}
export default (state = defaultState, action) => {
return state;
}
3.创建store
//===>src/store/index.js
import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer);
export default store;
4.在TodoList组件中,使用redux
import React, { Component } from 'react'
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store/index.js'
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState()
}
render() {
return (
<div style={{ marginTop: '10px', marginLeft: '10px' }}>
<div>
<Input value={this.state.inputValue} placeholder="todo info" style={{ width: '300px', marginRight: '10px' }} />
<Button type="primary">提交</Button>
</div>
<List
style={{ marginTop: '10px', width: '300px' }}
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
}
export default TodoList
效果
Action和Reducer的编写
1.为了更好的研究redux,我们需要在浏览器安装redux-devtools-extension这个插件(需要科学上网)。然后按照文档提示,我们在store文件中添加这样一句话:
//===>src/store/index.js
import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;
2.回忆一下这个图。我们现在要编写的功能是,在输入框输入内容(蓝色),然后点击确定,组件触发store的dispatch发送action(黄色)。store调用dispatch方法,将action发送给了store(橙色),store不能处理数据,所以他去找reducer仓库(紫色)修改数据。reducer通过传过来的action生成了新的state,return给store。最后store真正的拿到了新的数据,并会触发组件内容的改变。
回忆
下面我们实现这个过程。
3.修改TodoList
//===>src/TodoList.js
import React, { Component } from 'react'
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store/index.js'
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState()
this.handleInputChange = this.handleInputChange.bind(this)
this.handleStoreChange = this.handleStoreChange.bind(this)
this.handleBtnClick = this.handleBtnClick.bind(this)
store.subscribe(this.handleStoreChange)
}
render() {
return (
<div style={{ marginTop: '10px', marginLeft: '10px' }}>
<div>
<Input
value={this.state.inputValue}
placeholder="todo info"
style={{ width: '300px', marginRight: '10px' }}
onChange={this.handleInputChange} />
<Button type="primary" onClick={this.handleBtnClick}>提交</Button>
</div>
<List
style={{ marginTop: '10px', width: '300px' }}
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
handleInputChange(e) {
const action = {
type: 'change_input_value',
value: e.target.value
}
store.dispatch(action)
}
handleStoreChange(e) {
this.setState(store.getState())
}
handleBtnClick(e) {
const action = {
type: 'add_todo_item'
}
store.dispatch(action)
}
}
export default TodoList
4.修改reducer
//===>src/store/reducer.js
const defaultState = {
inputValue: '123',
list: [1, 2]
}
// reducer可以接收state,但是绝不能修改state
export default (state = defaultState, action) => {
if (action.type === 'change_input_value') {
const newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
if (action.type === 'add_todo_item') {
const newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue)
newState.inputValue = ''
return newState
}
return state;
}
我们配合插件看下效果
使用Redux完成TodoList删除功能
1.给List.Item添加点击事件
//===>src/TodoList.js
...
class TodoList extends Component {
constructor(props) {
...
}
render() {
return (
<div style={{ marginTop: '10px', marginLeft: '10px' }}>
...
<List
style={{ marginTop: '10px', width: '300px' }}
bordered
dataSource={this.state.list}
renderItem={(item, index) => <List.Item onClick={this.handleItemDelete.bind(this, index)}>{item}</List.Item>}
/>
</div>
)
}
...
handleItemDelete(index) {
const action = {
type: 'delete_todo_item',
index
}
store.dispatch(action)
}
}
export default TodoList
2.添加reducer中的删除逻辑
//===>src/store/reducer.js
const defaultState = {
inputValue: '',
list: []
}
// reducer可以接收state,但是绝不能修改state
export default (state = defaultState, action) => {
...
if (action.type === 'delete_todo_item') {
const newState = JSON.parse(JSON.stringify(state))
newState.list.splice(action.index, 1)
return newState
}
return state;
}
image.png
ActionTypes的拆分
在这个例子中 我们要把change_input_value、add_todo_item、delete_todo_item这些action弄成全局的。
1.编写actionTypes文件
//===>src/store/actionTypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
2.修改TodoList.js
//===>src/TodoList.js
...
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './store/actionTypes'
class TodoList extends Component {
...
handleInputChange(e) {
const action = {
type: CHANGE_INPUT_VALUE,
value: e.target.value
}
store.dispatch(action)
}
handleStoreChange(e) {
this.setState(store.getState())
}
handleBtnClick(e) {
const action = {
type: ADD_TODO_ITEM
}
store.dispatch(action)
}
handleItemDelete(index) {
const action = {
type: DELETE_TODO_ITEM,
index
}
store.dispatch(action)
}
}
export default TodoList
3.修改reducer.js
//===>src/store/reducer.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'
const defaultState = {
inputValue: '',
list: []
}
// reducer可以接收state,但是绝不能修改state
export default (state = defaultState, action) => {
if (action.type === CHANGE_INPUT_VALUE) {
const newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
if (action.type === ADD_TODO_ITEM) {
const newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue)
newState.inputValue = ''
return newState
}
if (action.type === DELETE_TODO_ITEM) {
const newState = JSON.parse(JSON.stringify(state))
newState.list.splice(action.index, 1)
return newState
}
return state;
}
使用actionCreator统一创建action
1.编写actionCreator
//===>src/store/actionCreators.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'
export const getInputChangeAction = (value) => ({
type: CHANGE_INPUT_VALUE,
value
})
export const getAddItemAction = () => ({
type: ADD_TODO_ITEM,
})
export const getDeleteItemAction = (index) => ({
type: DELETE_TODO_ITEM,
index
})
2.把actionCreator引入TodoList
//===>src/TodoList.js
import React, { Component } from 'react'
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store/index.js'
import { getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators'
class TodoList extends Component {
constructor(props) {
...
}
render() {
return (
...
)
}
handleInputChange(e) {
const action = getInputChangeAction(e.target.value)
store.dispatch(action)
}
handleStoreChange(e) {
this.setState(store.getState())
}
handleBtnClick(e) {
const action = getAddItemAction()
store.dispatch(action)
}
handleItemDelete(index) {
const action = getDeleteItemAction(index)
store.dispatch(action)
}
}
export default TodoList
Redux知识点复习补充
纯函数
纯函数指的是,给定固定的输入,就一定会有固定的输出,而且不会有副作用。
store是唯一的
只有store能够改变自己的内容
Reducer必须是顺函数
Redux的核心API
createStore
store.dispatch
store.getState
store.subscribe