React基础 -3 --Redux

课程目标

  • Redux 三大原则
  • redux 基础使用
  • react-redux 使用
  • redux-thunk 使用

Redux

Redux 是一个独立的 JavaScript 状态管理库,与非 React 内容之一
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。不仅于此,它还提供 超爽的开发体验,比如有一个时间旅行调试器可以编辑后实时预览。
Redux 除了和 React 一起用外,还支持其它界面库。 它体小精悍(只有2kB,包括依赖)。

中文网站:https://www.redux.org.cn/

  • npm install --save redux
  • npm install --save react-redux
  • npm install --save-dev redux-devtools

为什么使用 redux

React 本身 MVVM 渐进式框架(M(model 数据模型) - V(view 视图) - VM(虚拟模型))

  • 理解 Redux 核心几个概念与它们之间的关系
    - state
    - reducer
    - store
    - action

  • state 对象
    通常我们会把应用中的数据存储到一个对象树(Object Tree) 中进行统一管理,我们把这个对象树称为:state
    state 是只读的
    这里需要注意的是,为了保证数据状态的可维护和测试,不推荐直接修改 state 中的原数据
    通过纯函数修改 state
    什么是纯函数?

    1. 相同的输入永远返回相同的输出
    2. 不修改函数的输入值
    3. 不依赖外部环境状态
    4. 无任何副作用(异步请求,定时器等)

使用纯函数的好处
1. 便于测试
2. 有利重构

  • action 对象
    我们对 state 的修改是通过 reducer 纯函数来进行的,同时通过传入的 action 来执行具体的操作,action 是一
    个对象
    • type 属性 : 表示要进行操作的动作类型,增删改查……
    • payload属性 : 操作 state 的同时传入的数据

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

  • Store 对象
    为了对 state,Reducer,action 进行统一管理和维护,我们需要创建一个 Store 对象

  • redux 三大原则

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

注意:
1.reducer 中不能写异步请求,定时器等
2. dispatch是个同步方法

redux API
  • createStore(reducer, [preloadedState], enhancer);

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

    • reducer(state,action)
  • Store

    • getState()
    • dispatch(action)
    • subscribe(listener)
    • replaceReducer(nextReducer)
  • combineReducers(reducers) 将 reducer 函数拆分成多个单独的函数,拆分后的每个函数负责独立管理state 的一部分

  • applyMiddleware(…middlewares) 中间件 异步修改时用到

import React from 'react';
import {createStore} from "redux";

/*
纯函数:
    state: 就是状态,状态本身要有一个默认值,
  function reducer(state=state的默认值,action){

  }
*/
function reducer(state={},action){
    //console.log(state,action);
    switch(action.type){
        case "edit_name":
          return {
              ...state,///state本身的解构
              name: action.name//当前修改这一项
          } 
        case "edit_age":
            return {
                ...state,
                age: action.age
            }    
    }
    return state;
}
/*
  createStore(reducer) 用来创建一个仓库(store),在仓库中对我们的状态进行管理
  reducer 纯函数用来对 state 进行管理,纯函数的意思就是,它后续的操作是可预期的    
  reducer 就相当于管理员
*/
/**
 * 流程:
 * 管理员reducer , reducer中可以获取state以及action修改 ,将管理员扔给createStore,就创建了store仓库
 * 
 * - 通过dispatch发起一个修改,默认情况只接收一个对象,且必须有个type属性
 *  dispatch(action)
        action: {
            type:"edit_name",//比如修改type:'edit_name'
            name:"kkb"
        }
   - 通过getState获取state
   - subscribe监听state修改,可以直接监听每次的修改
 * 
 * 
 * 
 */
let store = createStore(reducer);

/**
 * 仓库,本身是一个对象,包含三个方法
  dispatch: ƒ dispatch(action) -- 发起一个修改动作
    dispatch(action)
        action: {
            type: "做了什么修改"
        }
  getState: ƒ getState() -- 获取 state
  replaceReducer: ƒ replaceReducer(nextReducer) -- 替换掉 reducer
  subscribe: ƒ subscribe(listener) -- 监听状态修改
 * 
 * 
 * */
store.subscribe(()=>{
    console.log(store.getState());///修改完打印
});
store.dispatch({
  type:"edit_name",
  name:"kkb"
});
//console.log(store.getState());
store.dispatch({
  type:"edit_age",
  age: 9
});
//console.log(store.getState());




function App() {
  return (
    <div className="App"></div>
  );
}

export default App;

一个项目只有一个store,但是如果页面特别多,怎么分开管理呢?

import React from 'react';
import {createStore,combineReducers} from "redux";
/*
页面较多:各个页面数据分开管理
  index 的数据要管理
  message 的数据要管理
  list 的数据要管理

*/
// function reducer (state = {
//   index:{info:"首页的数据"},
//   list:{info:"list的数据"},
//   message:{info:"message的数据"},
// }, action) {
//   // 在此对多页面的state的数据管理,太过复杂了
// }

// 随着应用变大,你可以把它拆成多个小的 reducers
function index(state={info:"首页的数据"},action){
  switch(action.type){
      case "index_edit_info":
          return {
            info: action.info
          }
  } 
  return state;
}
function list(state={info:"列表页的数据"},action){
  switch(action.type){
    case "list_edit_info":
        return {
          info: action.info
        }
} 
return state;
}
// function reducer(state={},action){
//   return {
//     index: index(state.index,action),///名称需要保持一致
//     list: list(state.list,action)
//   }
// }
let reducer = combineReducers({///组合reducer
  index,
  list
})
let store = createStore(reducer);
store.dispatch({
  type:"index_edit_info",
  info: "新的首页信息"
});
console.log(store.getState());

function App() {
  return (
    <div className="App"></div>
  );
}

export default App;


React-redux

把react和-redux结合

npm i -s react-redux

  • connect()------------高阶函数,传入数据,返回一个函数。7之前和类组件中常用用,7之后函数组件用下面的三个方法
  • useDispatch 获取dispatch
  • useStore 获取store
  • useSelector 获取state

使用
index.js
Provider在项目和路由的最外层

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from "react-redux";
// context跨组件传递信息的父级,被封装成了redux的Provider
import store from "./store/index";
import App from './App';

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>, 
    document.getElementById('root')
);

store /index.js

import {createStore,combineReducers} from "redux";
import data from "./reducer";
export default createStore(combineReducers({ data }));
/**
 * createStore和combineReducers
 * 创建仓库 
 * 
 */
function data(state={
    name:"kkb",
    age: 8
},action){
    switch(action.type){
        case "edit_name":
          return {
              ...state,
              name: action.name
          } 
        case "edit_age":
            return {
                ...state,
                age: action.age
            }    
    }
    return state;
}

export default data;
/**
 * 创建reducer
 */

app.js

import React from 'react';
import Inner from './inner';
function App() {
  return (
    <div className="App">
        <Inner info="哈哈" />
    </div>
  );
}

export default App;

inner.js

import React, { Fragment } from 'react';
import {connect} from "react-redux";
function Inner(props) {
    console.log(props);
  let {name,age,dispatch} = props;  
  return (
    <Fragment>
         <p>name:{name}</p> 
         <p>age:{age}</p>
         <button
            onClick={()=>{
                dispatch({///调用dispatch修改state
                    type:"edit_name",
                    name: "开课吧"
                });
            }}
         >修改名字</button>  
    </Fragment>
  );
}

/*
    在哪个组件中需要使用 redux 中的数据,就调用 connect 对这个组件进行包装
    
        connect(state的处理函数)(组件)
            state的处理函数(state[,props])=>{
                return 要传给组件的数据
            }

*/


export default connect((state,props)=>{
    //console.log(props);
    return state.data;//connect 参数是一个函数,这个函数必须返回一个对象,connect 会把这个对象传递给我们组件的props
})(Inner);使用hooks就不用在对组件进行包装了
  • useSelector() —获取state,接收一个函数,会把data返回
  • useStore()-----返回了整个stroe
  • useDispatch()----执行之后,会把dispatch方法返回,可以直接调用dispatch修改属性
import React, { Fragment } from 'react';
import { useSelector, useStore, useDispatch } from "react-redux";
/**
 * 
 * useSelector() ---获取state,接收一个函数,会把data返回
 * useStore()-----返回了整个stroe
 * useDispatch()----执行之后,会把dispatch方法返回,可以直接调用dispatch修改属性
 */
function Inner() {
  let {name,age} = useSelector(state=>{
        return state.data;
  });
  let dispatch = useDispatch();
  //console.log(useStore());
  //console.log();
  return (
    <Fragment>
         <p>name:{name}</p> 
         <p>age:{age}</p>
         <button
            onClick={()=>{
                dispatch({
                    type:"edit_name",
                    name:"开课吧"
                });
            }}
         >修改名字</button>  
    </Fragment>
  );
}


export default Inner;

redux-thunk 异步操作中间件

npm i redux-thunk

  • redux-thunk
    • 参数是对象,直接调用reducer修改我们的state
    • 参数是函数,调用该函数,并且把dispatch和getstate传递给函数
  • 使用
    • 引入thunk即 import thunk from “redux-thunk”;
    • 然后创建仓库时,通过applyMiddleware方法传入thunk,export default createStore(reducer, applyMiddleware(thunk));
    • 调用 dispatch(function(dispatch,getState){})

创建仓库

import {createStore,combineReducers,applyMiddleware} from "redux";
import data from "./reducer";
import thunk from "redux-thunk";
let reducer = combineReducers({data});
export default createStore(reducer, applyMiddleware(thunk));
/**
 * applyMiddleware为了可以引入异步中间件
 */

dispatch 异步修改

import React, { Fragment } from 'react';
import {useSelector,useStore,useDispatch} from "react-redux";
function Inner() {
  let {name,age,edit_state} = useSelector(state=>{
        return state.data;
  });
  let dispatch = useDispatch();
  return (
    <Fragment>
         <p>{edit_state}</p>
         <p>name:{name}</p> 
         <p>age:{age}</p>
         <button
            onClick={()=>{
                dispatch(function(dispatch,getState){
                    dispatch({
                        type:"edit_state",
                        state: "准备操作name"
                    });///给一个对象就直接去操作了
                    setTimeout(()=>{///异步做成
                      dispatch({
                          type:"edit_name",
                          name: "开课吧123"
                      });
                      dispatch({
                          type:"edit_state",
                          state: "无操作"
                      });
                    },2000);
                    //console.log(dispatch,getState);
                });//使用 thunk 中间件之后,dispatch 就可以接收一个函数,此时发出请求之后,不会直接去修改,此时可以去做异步请求了
            }}
         >修改名字</button>  
    </Fragment>
  );
}


export default Inner;

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值