先说下上图他们之间是怎么操作流通的:
1、当view需要发起行为时,需要在当前位置触发action,即store.dispatch(addNote())
2、当Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。而这种 State 的计算过程就叫做 Reducer。而Reducer方法是由store.dispatch方法触发来自动执行的。为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore方法。
import { createStore } from ‘redux’; const store = createStore(reducer);
Reducer 是一个函数,且为纯函数,它接受 Action 和当前 State 作为参数,Reducer 函数里面不能改变 State,必须返回一个全新的state对象。
3、state更新,触发view发生改变,而在此之前必须把数据store和操作事件action绑定到需要使用的组件上,就需要用到connect函数(connect方法可参考这篇文章:https://yq.aliyun.com/articles/59428):
意思是先接受两个参数(数据绑定mapStateToProps和事件绑定mapDispatchToProps),再接受一个参数(将要绑定的组件本身):
export default connect(mapStateToProps,mapDispatchToProps)(App)
其中:
mapStateToProps 就是将state作为props绑定到组件上
mapDispatchToProps是可选的,将 action 作为 props 绑定到组件上,如果不传这个参数redux会把dispatch作为属性注入给组件,可以手动当做store.dispatch使用
//mapDispatchToProps 为actions里面的函数绑定dispatch,可直接调用this.props.actions.xxx(),即等同于没绑定情况下this.props.dispatch(xxx()),不需要手动dispatch
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(actions, dispatch)
});
//mapStateToProps 可返回当前数组需要的几个state属性值
const mapStateToProps = state => {
return{
notes : state.notes
}
};
以上,便可以在组件内触发action,更新state,来更新view的变化
3. 创建store store文件夹下 index.js
import { createStore ,applyMiddleware ,compose} from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk)
);
const store = createStore(reducer, enhancer);
export default store;
- 编写action
定义action事件 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';
export const INIT_LIST_ACTION = 'init_list_action';
actionCreators.js
import { INIT_LIST_ACTION , CHANGE_INPUT_VALUE , ADD_TODO_ITEM , DELETE_TODO_ITEM} from './actionTypes';
import axios from 'axios';
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
});
export const getInitListAction = (data) => ({
type: INIT_LIST_ACTION,
data
});
export const getTodoList = () => {
return (dispatch) => {
axios.get('/list.json')
.then((res) => {
const data = res.data;
const action = getInitListAction(data);
dispatch(action);
})
.catch(() => {
alert('error')
});
}
}
编写reducer.js
import { CHANGE_INPUT_VALUE ,ADD_TODO_ITEM , DELETE_TODO_ITEM ,INIT_LIST_ACTION } 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 === INIT_LIST_ACTION) {
const newState = JSON.parse(JSON.stringify(state));
console.log(action.data);
newState.list = action.data;
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;
}
入口目录index.js
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
ReactDOM.render(<TodoList />, document.getElementById('root'));
TodoList.js
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import TodoListUI from './TodoListUI';
import store from './store'
import { getInputChangeAction ,getAddItemAction , getDeleteItemAction , getTodoList} from './store/actionCreators';
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);
this.handleItemDelete = this.handleItemDelete.bind(this);
store.subscribe(this.handleStoreChange);
}
render(){
return (
<TodoListUI
inputValue={this.state.inputValue}
list={this.state.list}
handleInputChange={this.handleInputChange}
handleBtnClick={this.handleBtnClick}
handleItemDelete={this.handleItemDelete}
/>
)
}
componentDidMount() {
const action = getTodoList();
store.dispatch(action);
}
handleInputChange(e){
const action = getInputChangeAction(e.target.value);
store.dispatch(action);
}
handleStoreChange(){
this.setState(store.getState());
}
handleBtnClick(){
const action = getAddItemAction();
store.dispatch(action);
}
handleItemDelete(index){
const action = getDeleteItemAction(index);
store.dispatch(action);
}
}
export default TodoList;
UI组件TodoListUI.js
import React from 'react';
import { Input , Button , List } from 'antd';
//无状态组件:当一个组件只含有render函数时,可以考虑使用无状态组件;
//无状态组件的性能要优于普通组件
const TodoListUI = (props) => {
return (
<div style={{marginTop:'10px',marginLeft:'10px'}}>
<div>
<Input
value={props.inputValue}
placeholder="todo info"
style={{width:'300px',marginRight:'10px'}}
onChange={props.handleInputChange}
/>
<Button type="primary" onClick={props.handleBtnClick}>提交</Button>
</div>
<List
style={{width:'300px', marginTop:'10px'}}
bordered
dataSource={props.list}
renderItem={(item,index) => (<List.Item onClick={ () => {props.handleItemDelete(index)}} key={item} >{item}</List.Item>)}
/>
</div>
)
}
export default TodoListUI;