个人随笔记录 仅为个人笔记。学习请直接翻看下面的参考链接。
1.redux中简单的关键词。
安装npm i redux react-redux redux-thunk redux-devtools-extension
- store 仓库。控制中心
- state 实际的数据
- reducer 就是一个纯函数
- Dispatch Actions。派发action来改变State
- 什么是Redux Action? Redux中,具有type属性的普通对象就被称为action。所以创造一个action很简单,因为action就是给开发人员用的。一般的action对象包含type属性(具体的动作),data属性(操作的参数)
let action={
type:'add',
data:1
}
-----------------------------
//不过通常这个type属性都是在外部定义常量。可维护性更好下面这种
const COUNT_ADD="count_add"
let action={
type:COUNT_ADD,
data:1
}
- 什么是Redux Dispatch? 我们创建的store中有一个内置的函数dispatch。可以store.dispatch。调用的时候需要有action作为参数。然后store.reducer时,会把action注入进去。返回的值就会更新state
2.快速代码。
在src下面创建一个redux文件夹
-src
-redux
action-types.js
actions.js
reducers.js
store.js
2.1 简单版本 代码部分。只有redux。
store.js
import { createStore } from "redux";
import reducer from "./reducers";
// 创建store的时候需要注入reducer.reducer就是返回state的函数。每次触发都会返回新的state
const store = createStore(reducer);
export default store;
reducers.js
reducer必须是一个纯函数。纯函数满足一下两个点 不依赖外部变量。就是说你传入100次同一个参数,100次的结果是一致的。 不会改变外部变量。不能有副作用。(所以你每次想用旧的state时,不要直接在旧的state上操作,然后返回旧的state)。这里需要你展现你高超的js深度克隆。唯一能做的就是{...}
// reducer 就是一个纯函数。函数就完事了。给createStore使用。
//store.dispatch(action)->store.reducer->然后这个action被注入了进来。然后就根据action.type想怎么操作就怎么操作吧。你可以用if/else或者switch。
function reducer(state = {name:'nihaoa'}, action) {
console.log("每次更新都打印一下", state);
switch (action.type) {
case "add":
let newState = JSON.parse(JSON.stringify(state));
newState.address = "ایرانیان 奥利给";
newState.count = action.data;
return newState;
default:
return state;
}
}
export default reducer;
上面这个reducer在store中的数据就是store.getState()==={name:'nihaoa'}
在组件中使用.import store from "./redux/store";
console.log(store.getState()); //可以看到初始化的state
<button
onClick={() => {
store.dispatch({ type: "add", data: 1 });//这里就能出发代码
}}
>
触发一下
</button>
2.2 redux+react-redux(增加了react-redux的契合度) 没有异步
毕竟你不能在每一个组件中都手动引入store。
所以react-redux提供Provider组件。用这个组件包裹整个应用。那么每一个组件都能访问Redux中的store。还有connect高级组件,可以帮你注入state和dispatch到props中。
action-types.js
/**
* 包含action type名称的常量
*/
export const INCREMENT = "increment"; //增加计数器
export const DECREMENT = "decrement"; //减少计数器
actions.js
// 引入常量action-type
import { INCREMENT, DECREMENT } from "./action-types";
// 同步的action.
export const increment = () => ({ type: INCREMENT });
export const decrement = () => ({ type: DECREMENT });
store.js
import { createStore } from "redux";
import reducer from "./reducers";
//开启redux插件 .没有redux-thunk异步中间件时
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
reducer.js
import { INCREMENT, DECREMENT } from "./action-types.js";
function reducer(state = { count: 0 }, action) {
switch (action.type) {
case INCREMENT:
const newCount = state.count + 1;
return { count: newCount };
case DECREMENT:
const newCount1 = state.count - 1;
return { count: newCount1 };
default:
return state;
}
}
export default reducer;
index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./redux/store";
import Main from "./Main";
ReactDOM.render(
// 通过props的形式注入。然后在所有子组件中都能拿到store
<Provider store={store}>
<Main></Main>
</Provider>,
document.querySelector("#root")
);
Main.js
import React from "react";
import { connect } from "react-redux";
import { increment, decrement } from "./redux/actions";
function Main(props) {
return (
<div>
<h2>Counter</h2>
<div>
<button
onClick={() => {
props.increment();
}}
>
增加
</button>
<span>{props.count}</span>
<button
onClick={() => {
props.decrement();
}}
>
减少
</button>
</div>
</div>
);
}
function mapStateToProps(state) {
// 返回的这个对象。Object.assign(props,state).并且还注入了streo.dispatch这个函数。
// 调用props.dispatch(actionFactory())即可。不过下面还可以优化
return state;
}
// 但是。每次调用props.dispatch(actionFactory())好像也是挺麻烦的。所以出现了mapDispatchToProps
// 传入对应的action工厂函数。mapDispatchToProps会将这个函数自动映射到props上。
// 调用工厂函数等于执行dispatch(actionFactory());
const mapDispatchToProps = {
increment,
decrement
};
export default connect(mapStateToProps, mapDispatchToProps)(Main);
2.3 redux+react-redux+redux-thunk 完整版
做一个发送请求的管理。开始发送。成功接收两种action,当然你还可以加上失败的action。
包括合并多个reducer合并。如果有必要,你可以在store创建后,立即store.dispatch
action-types.js
/**
* 包含action type名称的常量
*/
export const FETCH_DATA = "FETCH_DATA"; //发出请求的action
export const SUCCESS = "SUCCESS"; //成功的action
export const FAILE = "FAILE"; //失败的action
action.js
// 引入固定的变量
import { FETCH_DATA, SUCCESS, FAILE } from "./action-types";
// 同步的action
const startReq = () => ({ type: FETCH_DATA });
const success = msg => ({ type: SUCCESS, msg });
// 异步的action
export const getMsgAsync = () => {
// 会自动注入dispatch和state。但是一般都只用到了state
return dispatch => {
// 发送异步请求
dispatch(startReq());
setTimeout(() => {
dispatch(success("成功获取信息了"));
}, 2000);
};
};
reducer.js
import { FETCH_DATA, SUCCESS, FAILE } from "./action-types.js";
// 合并reducer
import { combineReducers } from "redux";
let initData = {
msg: "暂无信息"
};
let initArticle = {
msg: "文章的信息"
};
// 第一个reducer。实际上使用了这个
function getDataReducer(state = initData, action) {
switch (action.type) {
case FETCH_DATA:
return { ...state, msg: "正在请求信息中...." };
case SUCCESS:
return { ...state, msg: action.msg };
case FAILE:
return { ...state, msg: action.msg };
default:
return state;
}
}
// 这个reducer是作为合并后。看下state的格式。下面有图
function article(state = initArticle, action) {
switch (action.type) {
default:
return state;
}
}
export default combineReducers({
getDataReducer,
article
});
数据结构图片一目了然。
store.js
//因为要使用redux-thunk。所以需要从redux中的中间件
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import reducer from "./reducers";
// 最终版:可以是用插件。使用异步
const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)));
// 立刻执行
store.dispatch({ type: "now" });
export default store;
index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./redux/store";
import Main from "./Main";
ReactDOM.render(
// 通过props的形式注入。然后在所有子组件中都能拿到store
<Provider store={store}>
<Main></Main>
</Provider>,
document.querySelector("#root")
);
Main.jsx
import React from "react";
import { connect } from "react-redux";
import { getMsgAsync } from "./redux/actions";
function Main(props) {
return (
<div>
<h2>Counter</h2>
<div>
<span>{props.msg}</span>
<button
onClick={() => {
props.getMsgAsync();
}}
>
发送请求
</button>
</div>
</div>
);
}
function mapStateToProps(state) {
console.log(state);
return state.getDataReducer;
}
const mapDispatchToProps = {
getMsgAsync
};
export default connect(mapStateToProps, mapDispatchToProps)(Main);
redux-thunk 处理异步
就像vuex一样。也分同步和异步,但是vue都给你集成好了,用就完事了。
reducer是纯函数。不能再reducer中调用api或dispatch其它action。 action生成器中也不行。 redux-thunk出现了。可以让action生成器返回一个函数而不是简单的action对象。被返回的函数就是thunk action 而action生成器返回的函数接收两个参数:dispatch函数和getState
function doStuff() {
return function(dispatch, getState) {
// 在这里 dispatch actions
// 或者获取数据
// 或者该干啥干啥
}
}
扩展:插件查看redux数据状态
因为redux并不局限于在react中使用,所以并没有和react插件绑在一起。
插件名称:Redux DevTools
去谷歌扩展下载
不像vuex专属于vue一样。所以必须要安装插件。安装完成后,还需要在创建store时加上开启查看的权限
//开启redux插件 .没有redux-thunk异步中间件时
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
当使用了异步react-thunk这个中间件。代码如下
//因为要使用redux-thunk。所以需要从redux中的中间件
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import reducer from "./reducers";
// 最终版:可以是用插件。使用异步
const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)));
export default store;
#参考: [译] 2019 React Redux 完全指南juejin.cn/post/684490… 绝世好文。一点点跟着写,看每一步的数据状态,结构。