npm i redux
yarn add redux
理解 Redux 核心几个概念与它们之间的关系
为了对 state,Reducer,action 进行统一管理和维护,我们需要创建一个 Store 对象
import { createStore } from "redux";
function reducer(state = {}, action) {
return state;
}
const store = createStore(reducer);
console.dir(store);
调用 dispatch 方法,可以向 store 发起 action,
store 会调用 reducer,将 action 传递给 reducer
是一个同步方法
获取 store 中存储的 state
用于监听 state 变化,或取消监听,用于获取最新的state
import { createStore } from "redux";
function reducer(state = {
cnt: 1
}, action) {
console.log(action);
switch (action.type) {
case "add":
state.cnt++;
return state; // 把修改后的新的state返回
default:
break;
}
return state;
}
const store = createStore(reducer);
// console.dir(store);
store.subscribe(() => {
console.log("state修改了", store.getState());
});
setInterval(() => {
store.dispatch({
type: "add"
});
}, 1000);
通常我们会把应用中的数据存储到一个对象树(Object Tree) 中进行统一管理,我们把这个对象树称为:state
这里需要注意的是,为了保证数据状态的可维护和测试,不推荐直接修改 state 中的原数据
什么是纯函数?
使用纯函数的好处
import { createStore } from "redux";
function reducer(state = {
cnt: 1
}, action) {
console.log(action);
switch (action.type) {
case "ADD":
state.cnt++;
return state; // 把修改后的新的state返回
case "MINUS":
state.cnt--;
return state;
}
return state;
}
const store = createStore(reducer);
// console.dir(store);
window.onload = function () {
let addBtn = document.createElement("button");
addBtn.innerHTML = "++";
addBtn.onclick = () => {
store.dispatch({
type: "ADD"
});
};
document.body.appendChild(addBtn);
let p = document.createElement("p");
render();
document.body.appendChild(p);
let minusBtn = document.createElement("button");
minusBtn.innerHTML = "--";
minusBtn.onclick = () => {
store.dispatch({
type: "MINUS"
});
};
document.body.appendChild(minusBtn);
store.subscribe(render);
function render() {
console.log("state修改了", store.getState());
p.innerHTML = store.getState().cnt;
}
};
action 修改动作
我们对 state 的修改是通过 reducer 纯函数来进行的,同时通过传入的 action 来执行具体的操作
但是这里需要注意的是,我们不直接去调用 Reducer 函数,而是通过 Store 对象提供的 dispatch 方法来调用
react项目中的 redux 绑定库
使组件层级中的 connect() 方法都能够获得 Redux store
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from "react-redux";
import store from "./store/index";
import App from './app';
ReactDOM.render(
<Provider store={store}>
<App />,
</Provider>,
document.getElementById('root')
);
let cb = connect((state) => {...state})
import React, { Component } from 'react';
import "./index.css";
import AddMessage from './addMessage';
import MessageList from './messageList';
import { connect } from 'react-redux';
function App(props) {
console.log(props);
let { data } = props;
return <section className="wrap">
<h2 className="title">留言板</h2>
<AddMessage />
{data.length > 0 ? <MessageList
data={data}
/> : ""}
</section>;
}
App = connect(state => state)(App);
export default App;
用于获取state
useSelector(state => 我们需要的state相关数据
import React from 'react';
import "./index.css";
import AddMessage from './addMessage';
import MessageList from './messageList';
import { useSelector } from 'react-redux';
function App() {
let data = useSelector(state => state);
return <section className="wrap">
<h2 className="title">留言板</h2>
<AddMessage />
{data.length > 0 ? <MessageList /> : ""}
</section>;
}
export default App;
import React, { Component } from 'react';
import "./index.css";
import AddMessage from './addMessage';
import MessageList from './messageList';
import { useSelector, useDispatch, useStore } from 'react-redux';
function App() {
let data = useSelector(state => state);
let dispatch = useDispatch();
let store = useStore();
console.log(data, dispatch, store);
return <section className="wrap">
<h2 className="title">留言板</h2>
<AddMessage />
{data.length > 0 ? <MessageList
data={data}
/> : ""}
</section>;
}
export default App;
留言store
设置store创建仓库
设置action 方法
import { createStore } from "redux";
function reducer(state = [{
id: 0,
name: "昵称",
message: "留言内容留言内容留言内容留言内容留言内容1"
}, {
id: 1,
name: "昵称22",
message: "留言内容容留言内容留言内容留言内容留言内容留言内容33"
}], action) {
switch (action.type) {
case "ADD_MESSAGE":
return [{
id: Date.now(),
name: action.name,
message: action.message
}, ...state];
case "REMOVE_MESSAGE":
return state.filter(item => item.id !== action.id);
}
return state; //初始化
}
const store = createStore(reducer);
export default store;
组件获取store中的state,渲染数据
import React from "react";
import Message from "./message";
import { useSelector } from "react-redux";
export default function MessageList() {
let data = useSelector(state => state);
return <ul className="messageList">
{
data.map(item => <Message key={item.id} data={item} />)
}
</ul>;
}
组件dispatch提交方法
import React from "react";
import { useDispatch } from "react-redux";
export default function Message(props) {
let { data } = props;
let { id, name, message } = data;
let dispatch = useDispatch();
return <li>
<h3>{name}</h3>
<p>{message}</p>
<a onClick={() => {
dispatch({
type: "REMOVE_MESSAGE",
id
});
}}>删除</a>
</li>;
}
更新的过程中,去做一些其他的事情,
dispatch —> reducer 更新state
dispatch --> 中间件 --> reducer
因为dispatch是一个同步方法,想要一步获取数据的时候就需要异步操作
function List() {
let { type = "all" } = useParams();
let { loading, data } = useSelector(state => state);
let dispatch = useDispatch();
useEffect(() => {
dispatch({
type: "LOADING"
});
axios.get(`https://cnodejs.org/api/v1/topics?page=1&tab=${type}&limit=10`)
.then(res => {
dispatch({
type: "LOAD",
data: res.data.data
});
}, () => {
dispatch({
type: "LOAD",
data: []
});
});
}, [type]);
console.log(data);
return (<Fragment>
{loading ?
"数据获取中……"
:
(
data.length > 0 ? (
<ul className="list">
{data.map(item => (<li key={item.id}>{item.title}</li>))}
</ul>
) : "暂无数据"
)}
</Fragment>
);
}
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
function reducer(state = {
data: [],
loading: false
}, action) {
switch (action.type) {
case "LOADING": // 正在获取数据
return {
data: [],
loading: true
};
case "LOAD"://数据获取完成有数据
return {
data: action.data,
loading: false
};
}
return state;
}
//添加一个中间件
const store = createStore(reducer, applyMiddleware(thunk));
export default store;
可以把所有的action提取出去,减少组件内部逻辑
import React, { Fragment, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import { getListData } from "./store/action";
function List() {
let { type = "all" } = useParams();
let { loading, data } = useSelector(state => state);
let dispatch = useDispatch();
useEffect(() => {
dispatch(getListData(type));
}, [type]);
return (<Fragment>
{loading ?
"数据获取中……"
:
(
data.length > 0 ? (
<ul className="list">
{data.map(item => (<li key={item.id}>{item.title}</li>))}
</ul>
) : "暂无数据"
)}
</Fragment>
);
}
export default List;
抽离出来的dispatch方法
import axios from "axios";
function getListData(type) {
return (dispatch, getState) => {
dispatch({
type: "LOADING"
});
axios.get(`https://cnodejs.org/api/v1/topics?page=1&tab=${type}&limit=10`)
.then(res => {
dispatch({
type: "LOAD",
data: res.data.data
});
}, () => {
dispatch({
type: "LOAD",
data: []
});
});
};
}
export { getListData };
let getListData = useListData();
useEffect(() => {
getListData(type);
}, [type]);
useListData
import { useDispatch } from "react-redux";
import axios from "axios";
function useListData() {
let dispatch = useDispatch();
return (type) => {
dispatch({
type: "LOADING"
});
axios.get(`https://cnodejs.org/api/v1/topics?page=1&tab=${type}&limit=10`)
.then(res => {
dispatch({
type: "LOAD",
data: res.data.data
});
}, () => {
dispatch({
type: "LOAD",
data: []
});
});
};
}
export { useListData };
当state数据多的时候会比较乱
function index(state = {
list: {},
topic: {},
user: {}
}, action) {
return {
list: list(state.list, action),
topic: topic(state.topic = {}, action),
user: user(state.user = {}, action)
};
}
所以可以将每个reducer抽离出来,使得逻辑清晰
function index(state = {
list: {},
topic: {},
user: {}
}, action) {
return {
list: list(state.list, action),
topic: topic(state.topic = {}, action),
user: user(state.user = {}, action)
};
}
function list(list = {}, action) {
return list;
}
function topic(topic = {}, action) {
return topic;
}
function user(user = {}, action) {
return user;
}
export default index;