Redux简介

Redux 是一个独立的 JavaScript 状态管理库,不依赖于任何其他库

1. 安装

npm i redux
yarn add redux

2. Redux核心概念

1)state:状态
通常我们会把应用中的数据存储到一个对象树(Object Tree) 中进行统一管理,我们把这个对象树称为:state

state 是只读的,这里需要注意的是,为了保证数据状态的可维护和测试,不推荐直接修改 state 中的原数据

2)reducer:纯函数
什么是纯函数?

  • 相同的输入永远返回相同的输出
  • 不修改函数的输入值
  • 不依赖外部环境状态,只依赖其参数
  • 无任何副作用

使用纯函数的好处:

  • 便于测试
  • 有利重构

3)store:仓库
为了对 state,Reducer,action 进行统一管理和维护,我们需要创建一个 Store 对象

  • getState: ƒ getState() // 获取 redux 存储的状态
  • dispatch: ƒ dispatch(action) // 发起一个修改
    调用 dispatch,store 会调用其绑定的 reducer 函数,并且会将当前的 state 和 action 传入 reducer。然后获取到 reducer 的返回值,指定为仓库的 state
  • replaceReducer: ƒ replaceReducer(nextReducer) // 替换掉当前的 reducer
  • subscribe //监听状态发生改变

4)action:动作
action 是一个纯对象(普通对象),action 必须有一个 type 属性,用于描述我们对 state 做出何种修改
我们对 state 的修改是通过 reducer 纯函数来进行的,同时通过传入的 action 来执行具体的操作,action 是一个对象

  • type 属性 : 表示要进行操作的动作类型,增删改查……
  • payload属性 : 操作 state 的同时传入的数据

但是这里需要注意的是,我们不直接去调用 Reducer 函数,而是通过 Store 对象提供的 dispatch 方法来调用

3.redux三大原则

  • 单一数据源: 整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中
  • state 是只读的: 唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象
  • 使用纯函数来执行修改

4.redux API

1)createStore(reducer, [preloadedState], enhancer);

  • reducer (Function): 接收两个参数,分别是当前的 state 树和要处理的 action,返回新的 state 树。
  • [preloadedState] (any): 初始时的 state。 在同构应用中,你可以决定是否把服务端传来的 state 后传给它,或者从之前保存的用户会话中恢复一个传给它。如果你使用 combineReducers 创建 - reducer,它必须是一个普通对象,与传入的 keys 保持同样的结构。否则,你可以自由传入任何 reducer 可理解的内容。
  • enhancer (Function): Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。这与 middleware 相似,它也允许你通过复合函数改变 store 接口。
  • 返回值 (Store): 保存了应用所有 state 的对象。改变 state 的惟一方法是 dispatch action。你也可以 subscribe 监听 state 的变化,然后更新 UI。
import { createStore } from "redux";

const store = createStore(reducer);

2)reducer

  • reducer(state,action)
function reducer(state = {
  count: 1
}, action) {
  //console.log(state,action);
  switch (action.type) {
    case "add":
      return {
        count: state.count + 1
      }
    case "minus":
      return {
        count: state.count - 1
      }
  }
  return state;
}

3)Store

  • getState()
  • dispatch(action)
  • subscribe(listener)
  • replaceReducer(nextReducer)
let unSubscribe = store.subscribe(() => {
  render();
});
render();
function render() {
  ReactDOM.render(<div>
    <p>{store.getState().count}</p>
    <button
      onClick={() => {
        store.dispatch({
          type: "add"
        })
      }}
    >+</button>
    <button
      onClick={() => {
        store.dispatch({
          type: "minus"
        })
      }}
    >-</button>
    <button onClick={unSubscribe}>取消监听</button>
    <button onClick={() => {
      unSubscribe = store.subscribe(() => {
        render();
      });
    }}>添加监听</button>
  </div>,
    document.querySelector("#root")
  );
}

4)combineReducers(reducers)
将 reducer 函数拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分

import { createStore,combineReducers } from "redux";
import count from "./count";
import todo from "./todo";

const store = createStore(combineReducers({count,todo}));
export default store;

5)applyMiddleware(...middlewares) 中间件

中间件:更新的过程中,去做一些其他的事情

  • dispatch ---> reducer 更新state
  • dispatch --> 中间件 --> reducer

异步操作中间件:redux-thunk

  • 参数是对象,直接调用 reducer 修改我们的 state
  • 参数是函数,调用该函数,并且把 dispatch 和 getState 传递我们的函数,可以在函数中,进行异步操作

store/store.js

import { createStore,combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import count from "./count";
import todo from "./todo";
import topics from "./topics"
const store = createStore(combineReducers({count,todo,topics}),applyMiddleware(thunk));
export default store;

store/topics.js

import axios from "axios";

function topicsLoading(d,g) {
   const {type} = g().topics;
   return axios.get(`https://cnodejs.org/api/v1/topics?tab=${type}&page=1&limit=20`)
   .then(res=>{
     d({
       type: "topics/load",
       data:res.data.data
     })
   })
}

export {topicsLoading}

topics/topics.js

import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { topicsLoading } from "../store/topics";
import List from "./list";
import Nav from "./navs";

function Topics() {
  const {loading,type,data} = useSelector(state=>state.topics);
  const d = useDispatch();
  useEffect(()=>{
    d(topicsLoading);
  },[type]);
  return <div style={{
    textAlign:"center"
  }}>
    <Nav />
    {loading?<h3>数据请求中……</h3>:<List data={data} />}
    
  </div>
}

export default Topics;

5. react-redux

react项目中的 redux 绑定库
1)安装

npm i react-redux

2)<Provider store>

import ReactDOM from "react-dom";
import {Provider} from "react-redux";
import App from "./App";
import store from "./store/store";

ReactDOM.render(
  <Provider
    store={store}
  >
    <App />
  </Provider>,
  document.querySelector("#root")
);

3)connect()
在组件中获取 react-redux 传递的 store 中的数据和操作

connect(mapStateToProps:()=>{})
mapStateToProps:根据 store 中的 state,映射出 要传递给组件的 props
mapStateToProps 的返回值会传递给组件,mapStateToProps 的返回值必须是一个对象

import {connect} from "react-redux";

function Count(props) {
  //console.log(props);
  const {count,dispatch} = props;
  return <>
    <p>{count}</p>
    <button onClick={()=>{
      dispatch({
        type: "add"
      })
    }}>+</button>
    <button onClick={()=>{
      dispatch({
        type: "minus"
      })
    }}>-</button>
  </>
}

export default connect(state=>state)(Count);

4)hooks

  • useDispatch 获取 dispatch
  • useStore 获取 store
  • useSelector 获取 state
import {useDispatch, useSelector, useStore} from "react-redux";

function Count() {
  const count = useSelector(state=>state.count);
  const d = useDispatch();
  console.log(useStore());
  return <>
    <p>{count}</p>
    <button onClick={()=>{
      d({
        type: "add"
      })
    }}>+</button>
    <button onClick={()=>{
      d({
        type: "minus"
      })
    }}>-</button>
  </>
}

export default Count;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容