Redux安装命令:npm i redux react-redux --save-dev
Redux流程:首先我们需要一个store,然后组件从store中去取数据,如果组件想改变store里的数据——首先需要派发一个action给store,store把action和之前的数据一起给到reducer,reducer结合action和之前的数据返回一个新数据给store,store更新自己的数据后告诉组件数据被更新,页面就会跟着联动。
创建Store目录:
父组件TodoList.js以及子组件TodoListUI.js部分(拆分代码实现redux集中管理数据,用无状态的UI组件——子组件做渲染,容器组件——父组件做逻辑):
//父
import React, { Component } from 'react';
import TodoListUI from './TodoListUI';
import axios from 'axios'
import store from './store'
import { getInputChangeAction, getAddItemAction, getDeleteItemAction, initListAction } from './store/actionCreators'
class TodoList extends Component {
//类中必有constructor函数 而且会最先执行
constructor(props) {
super(props);
this.state = store.getState();
this.handleInputChange = this.handleInputChange.bind(this);
this.handleStoreChange = this.handleStoreChange.bind(this);
this.handleButtonSubmit = this.handleButtonSubmit.bind(this);
this.handleItemDelete = this.handleItemDelete.bind(this);
}
componentDidMount() {
//3.固定——订阅store的变化,只要store中的数据改变,这个方法就会被执行
store.subscribe(this.handleStoreChange)
//请求数据存到store中 1.action给store传话
axios.get('/api/list').then((res) => {
const action = initListAction(res.data)
store.dispatch(action);//store.dispatch方法把action派发给store
}).catch((err) => {
console.log(err);
})
}
//4.固定——store已被改变后要做的事——当感知到store数据变化的时候,用setState调用store.getState方法,从store里重新取数据,替换掉当前组件中的数据
handleStoreChange() {
this.setState(store.getState(), () => {
console.log(this.state);//打印出的就是最新的数据
console.log(store.getState());//打印出的就是最新的数据
})
}
//改变input框值 1.action给store传话
handleInputChange(e) {
// const action = {//store!帮我改变inputValue的值,值为e.target.value
// type: change_input_value,
// value: e.target.value
// }
const action = getInputChangeAction(e.target.value)
store.dispatch(action);//store.dispatch方法把action派发给store,store再默默传给reducer
}
//添加 1.action给store传话
handleButtonSubmit() {
const action = getAddItemAction();
store.dispatch(action)
}
//删除 1.action给store传话
handleItemDelete(index) {
const action = getDeleteItemAction(index)
store.dispatch(action)
}
render() {
return (
<TodoListUI
inpuValue={this.state.inpuValue}
handleInputChange={this.handleInputChange}
handleButtonSubmit={this.handleButtonSubmit}
list={this.state.list}
handleItemDelete={this.handleItemDelete} />
)
}
}
export default TodoList;
//子
import React from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
const TodoList = (props) => {
return (
<div style={{ marginTop: "10px", marginLeft: "10px" }}>
<div>
<Input
value={props.inputValue}
placeholder="todo info"
style={{ width: "300px", marginRight: "10px" }}
onChange={props.handleInputChange}></Input>
<Button type="primary" onClick={props.handleButtonSubmit}>提交</Button>
</div>
<List
style={{ marginTop: "10px", width: "370px" }}
bordered
dataSource={props.list}
renderItem={(item, index) =>
<List.Item
onClick={() => { props.handleItemDelete(index) }}>
{item}
</List.Item>
}
/>
</div>
)
}
export default TodoList;
但是,如果用了provider和connect这两个react-redux的核心api,那么将会在组件中定义mapStateToProps和mapDispatchToProps规则,并且组件可以因此写成无状态UI组件,以便于提高性能。主入口文件index.js和父组件TodoList.js将为以下代码:
//*** src > index.js
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
import reportWebVitals from './reportWebVitals';
//1.react-redux的第一个核心api是Provider,它连接store,provider内部的组件都有能力获取到store的内容了
import { Provider } from 'react-redux';
import store from './store'
const App = (
<Provider store={store}>
<TodoList />
</Provider>
)
ReactDOM.render(
App,
document.getElementById('root')
);
reportWebVitals();
//*** src > TodoList.js
import React, { Component } from 'react';
//*connect是react-redux的第二个核心Api,通过connect方法获取store的数据
import { connect } from 'react-redux';
import { changeInputAction, handleSubmitAction, handleDeleteAction } from './store/actionCreators'
//改为无状态UI组件
const TodoList = (props) => {
const { inputValue, changeInputValue, handleSubmitButton, handleDeleteItem, list } = props;
return (
<div>
<div>
<input value={inputValue} onChange={changeInputValue} type="text" />
<button onClick={handleSubmitButton}>提交</button>
</div>
<ul>
{
list.map((item, index) => {
return <li onClick={() => { handleDeleteItem(index) }} key={index}>{item}</li>
})
}
</ul>
</div>
)
}
//本来是有状态组件
// class TodoList extends Component {
// render() {
// const { inputValue, changeInputValue, handleSubmitButton, handleDeleteItem, list } = this.props;
// return (
// <div>
// <div>
// <input value={inputValue} onChange={changeInputValue} type="text" />
// <button onClick={handleSubmitButton}>提交</button>
// </div>
// <ul>
// {
// list.map((item, index) => {
// return <li onClick={() => { handleDeleteItem(index) }} key={index}>{item}</li>
// })
// }
// </ul>
// </div>
// )
// }
// }
//3.1规则1: store里的数据和组件的数据的关系;函数中传的state就是store里的数据
const mapStateToProps = (state) => {
return {
inputValue: state.inputValue,
list: state.list
}
}
//3.2规则2: 组件里props如何对store里的数据做修改和store.dispatch方法做关联;函数中传的dispatch就是让 changeInputValue函数可以调用store.dispatch方法改变数据
const mapDispatchToProps = (dispatch) => {
return {
changeInputValue(e) {
const action = changeInputAction(e.target.value);
dispatch(action);
},
//添加
handleSubmitButton() {
const action = handleSubmitAction();
dispatch(action);
},
//删除
handleDeleteItem(index) {
const action = handleDeleteAction(index);
dispatch(action)
}
}
}
//2.让TodoList这个组件和store做关联,没问题,因为在index.js主入口中,Provider组件包裹了TodoList组件
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
store>index.js部分:
import { createStore } from 'redux';
import reducer from './reducer';
//**当组件调用store.dispatch(action)方法时,store.js不知道要干什么,于是就把previousState和action默默传给reducer.js;之后 reducer.js返回newState给store,也是store在改变数据
//createStore创建store
const store = createStore(//把reducer传给store,store才知道数据有多少
reducer,//如果浏览器安装了redux的devtools那么就使用
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;
actionCreators.js部分:
//action对象是写在actionCreater里面的
import { change_input_value, add_todo_item, delete_todo_item, init_list_action } from './actionTypes'
export const getInputChangeAction = (val) => ({
type: change_input_value,
//value为action对象的属性
value: val
})
export const getAddItemAction = () => ({
type: add_todo_item
})
export const getDeleteItemAction = (index) => ({
type: delete_todo_item,
index
})
//函数接收组件传的参数——这里为axios请求的数据,返回对象就是我们要在reducer.js中使用的的action
export const initListAction = (data) => ({
type: init_list_action,
data
})
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'
reducer.js部分
//创建reducer——存储数据的笔记本
import { change_input_value, add_todo_item, delete_todo_item, init_list_action } from './actionTypes'
const defaultState = {//store的默认值
inputValue: '',
list: []
}
const reducer = (state = defaultState, action) => {
//2.reducer可以接收state,但是绝不能修改state,这也是为什么深拷贝
if (action.type === change_input_value) {
const newState = JSON.parse(JSON.stringify(state));//深拷贝
newState.inputValue = action.value;//改变store中inputValue的值
return newState//newState被返回给了store
}
//增加
if (action.type === add_todo_item) {
const newState = JSON.parse(JSON.stringify(state));//深拷贝
newState.inputValue && newState.list.push(newState.inputValue)//将目前的inputValue放进列表中
newState.inputValue = "";//并且把input框清空
return newState//newState被返回给了store
}
//删除
if (action.type === delete_todo_item) {
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.index, 1);
return newState//newState被返回给了store
}
//axios请求的数据作为list的值
if (action.type === init_list_action) {
const newState = JSON.parse(JSON.stringify(state));//深拷贝
newState.list = action.data;
return newState//newState被返回给了store
}
return state
}
export default reducer;