Redux

1.1 概念

1.1.1 Redux 是什么

  • Redux 专注于状态管理,和React解耦(没有逻辑上的联系和依赖,可以分开使用)
  • 单一状态,单项数据流
  • 核心概念:store, state, action, reducer

1.1.2 主要功能

  • 有一个保险箱(store),所有人的状态在那里都有记录(state
  • 需要改变的时候,需要告诉 dispatch 要干什么(action
  • 处理变化的 reducer 拿到 stateaction,生成新的 state

1.1.3 工作流程

  • 首先通过 reducer 新建 store,随时通过 store.getState 获取状态
  • 需要状态变更,store.dispatch (action) 来修改状态
  • Reducer 函数接受 state 和 action,返回新的 state,可以用 store.subscribe 监听每次修改

![1553935619355](F:\我的学习\My Study\07-React\assets\1553935619355.png)

1.1.4 Redux的使用

1.创建 store
/* store.js */
import { createStore } from 'redux';
const store = createStore()
2. 创建 Action
/* action.js */

const LOGIN = "LOGIN"

export function loginAction (loginData) {
  return { 
    type: LOGIN,
    loginData // {status: false, user: 'xiaodingyang', password: '123456'}
  } 
}
3. 创建 reducer
/* reducer.js */

// state中的默认的数据
let initData = {
  msg: []
}
export function login (state = initData, action) {
  switch (action.type) {
    case LOGIN:
      return { ...state, msg: action.loginData }
    default:
      return {...state}
  }
}
4. 将 reducer 给 store
import reducer form './reducer';
import { createStore } from 'redux';
const store = createStore(reducer);
5. 将store和组件关联
ReactDOM.render(
  <Provider store={store}><App /></Provider>
  , document.getElementById('root'));
  • 然后就是使用了,使用会在下面具体展示

1.1.5 store的API

1. store.getState()
  • 获取state的状态

    console.log(store.getState()) 
    
2. store.subscribe()
  • 订阅 state 变化

    const unsubscribe = store.subscribe(()=>{
      console.log(state.getState())
    })
    unsubscribe() // 执行返回值可取消订阅
    
3. store.dispatch()
  • 提交 action

    store.dispatch(loginAction())
    
4. 最终 store.js
  • 我们在main.js中导入store.js,就会执行以下代码

    import { createStore } from 'redux';
    import { loginAction } from 'action.js';
    
    const store = createStore()
    console.log(store.getState()) 
    const unsubscribe = store.subscribe(()=>{
      console.log(state.getState())
    })
    unsubscribe() // 执行返回值可取消订阅
    store.dispatch(loginAction())
    

1.2 React-redux

1.2.1 安装

npm i react-redux --save
  • 安装完 react-redux 以后,就可以:
    • 忘记 subscribe,记住 reduceractiondispatch
    • React-redux 提供 Providerconnect 两个接口来连接

1.2.2 具体使用

  • Provider向根组件注入 Store,Provider 组件在应用最外层,传入 store 即可,只使用一次

  • connect连接 React 组件和 Redux 状态层,Connect 负责从外部获取组件需要的参数, Connect 可以用装饰器的方式来写

    /* index.js */
    
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    
    // 引入redux
    import { createStore, applyMiddleware } from 'redux';
    import { counter } from './index-redux';
    import { Provider } from 'react-redux';
    
    const store = createStore(counter)  //新建 store
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    
    /* index-redux */
    
    // 根据老的状态和action生成新的状态
    export function counter (state = 0, action) {
      switch (action.type) {
        case '加':
          return state + 1;
        case '减':
          return state - 1;
        default:
          return state
      }
    }
    
    export function add () {
      return { type: '加' }
    }
    export function reduce () {
      return { type: '减' }
    }
    
    /* App.js */
    
    import React, { Component } from 'react'
    import { Button } from 'antd-mobile';
    import { add, reduce } from './index-redux';
    import { connect } from 'react-redux'
    
    class App extends Component {
      render () {
        let num = this.props.num
        return (
          <div className="App">
            <Button type="primary" inline onClick={() => this.props.reduce()}>-</Button>
            <Button type="warning" inline>{num}</Button>
            <Button type="primary" inline onClick={() => this.props.add()}>+</Button>
          </div>
        )
      }
    }
    
    // 将 state 值赋值给 num 
    const mapStateProps =  (state)=> {
      return { num: state }
    }
    
    const actionCreators = { add, reduce }
    
    // 使用 connect 连接,传入了两个参数,直接挂在到 props 上面,在页面就可以通过 this.props 访问。而且都不用写 dispatch 了
    App = connect(mapStateProps, actionCreators)(App) //将 App 注入 connet 从新生成新的 App
    export default App;
    

1.2.3 装饰器优化connect

1. npm run eject 弹出个性化配置
2. 安装插件
npm install babel-plugin-transform-decorators-legacy  --save-dev
npm install  @babel/plugin-proposal-decorators --save-dev
3. package.jsonbabel 加上plugins 配置
"plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
  • 将 connect 改写
4. 修改前
const mapStateProps =  (state)=> {
  return { num: state }
}
const actionCreators = { add, reduce }
App = connect(mapStateProps, actionCreators)(App)
5. 等价于(修改后)
const mapStateProps =  (state)=> {
  return { num: state }
}
const actionCreators = { add, reduce }
@connect(mapStateProps, actionCreators)
6. 等价于(修改后)
@connect(
  (state) => {
    return { num: state }	//state 里面属性放到 props
  },
  { add, reduce }	// state 里面方法放到props, 自动 dispatch
)
7. 注意:
  • 如果是 vscode 需要将以下设置修改为 true
"javascript.implicitProjectConfig.experimentalDecorators": true
  • 在使用 @connect 的时候一定要这样导出:
@connect(
  state =>  state,	//state 里面属性放到 props
  { add, reduce, addAsync }	// state 里面方法放到props, 自动 dispatch
)
class App extends Component {
  render () {
    return (
      <div className="App"></div>
    )
  }
}
export default App;

1.2.4 整个 redux 过程

/* index.js */

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// 引入redux
import { createStore } from 'redux';
import { counter } from './index-redux';
import { Provider } from 'react-redux';

const store = createStore(counter)  //新建 stor
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
/* index-redux */

// 根据老的状态和action生成新的状态 (reducer)
export function counter (state = 0, action) {
  switch (action.type) {
    case '加':
      return state + 1;
    case '减':
      return state - 1;
    default:
      return state
  }
}

// action
export function add () {
  return { type: '加' }
}
export function reduce () {
  return { type: '减' }
}
App.js
import React, { Component } from 'react'
import './App.css';
import { Button } from 'antd-mobile';
import { add, reduce } from './index-redux'
import { connect } from 'react-redux'

@connect(
  (state) => {
    return { num: state }
  },
  { add, reduce }
)
  
class App extends Component {
  render () {
    let num = this.props.num

    return (
      <div className="App">
        <Button type="primary" inline onClick={() => this.props.reduce()}>-</Button>
        <Button type="warning" inline>{num}</Button>
        <Button type="primary" inline onClick={() => this.props.add()}>+</Button>
      </div>
    )
  }
}

export default App;

1.2.5 combineReducers 拆分 reducer

  • 复杂 redux 应用,多个 reducer,用 combineReducers 合并
  • 新建一个 reducer.js 文件
/* reducer.js */
import { Auth } from "./component/LoginComponent/login-redux";
import {Counter } from "./component/CounterComponent/counter-redux";
import { combineReducers } from "redux";

export default combineReducers({Counter, Auth})
  • index.js 中引入使用。
import Reducer from './reducer';

//新建 store
const store = createStore(Reducer)  

ReactDOM.render(
  <Provider store={store}>
    <BrowserRouter>
      <Switch>
        <Route path="/" component={Home}></Route>
        <Route path="/login" component={LoginComponent}></Route>
      </Switch>
    </BrowserRouter>
    <App />
  </Provider>
  , document.getElementById('root'));

1.2.6 异步处理

  • 发起action请求是同步的,当我们发起ajax请求的时候,会立即action而不会等到返回数据以后再action。Redux默认只处理同步,异步任务需要 redux-thunk 中间件
  • 使用 applyMiddleware 开启中间件
  • Action 可以返回函数,使用 dispatch 提交 action
  • 安装:
npm i redux-thunk --save
/* index.js */

import { createStore, applyMiddleware } from 'redux';	// applyMiddleware 开启redux-thunk
import  thunk  from 'redux-thunk';

const store = createStore(Reducer, applyMiddleware(thunk))  
/* index-redux.js  */

export function addAsync () {
    return dispatch=>{
      getData().then(res=>{{
        dispatch(add(res)) // 将返回的数据传过去
      })
    }
}
  • es6 简写
/* index-redux.js  */

export addAsync = ()=> dispatch => {
      getData().then(res=>{{
        dispatch(add(res)) // 将返回的数据传过去
      })
    }

1.2.7 redux-devtools 调试工具

  • 新建 store 的时候判断 window.devToolsExtension
  • 使用 compose 结合 thunkwindow.devToolsExtension
  • 调试窗的 redux 选项卡,实时看到 state
import { createStore, applyMiddleware, compose } from 'redux';
const store = createStore(Reducer, compose(
  applyMiddleware(thunk),
  window.devToolsExtension ? window.devToolsExtension : () => { }))

1.3 Redux 进阶

1.1.1 Redux 项目结构组织方式

  • Ducks 方式

  • reducers、action、types、actions 组合到一个文件中,作为独立模块

  • 划分模块依据:应用状态 State,而不是界面功能。

  • 主要使用

    const LOGIN = "LOGIN"
    const LOGOUT = "LOGOUT"
    
    // Reducer
    let initData = {
      status: false,
      user: "肖定阳",
      password: "123456"
    }
    export function login (state = initData, action) {
      switch (action.type) {
        case LOGIN:
          return { ...state, status: true }
        case LOGOUT:
          return { ...state, status: false }
        default:
          return state
      }
    }
    
    // Action 在使用的时候就不需要一个一个的导入了
    export const actions = {
      log () {
        return { type: LOGIN }
      },
      logout () {
        return { type: LOGOUT }
      }
    }
    

1.1.2 redux的state 设计原则

1. 常见的两种错误
  • 以 API 为设计 State 的依据
  • 以页面 UI 为设计 State 的依据
2. 设计数据库基本原则
  • 数据按照领域分类,存储在不同的表中,不同的表中存储的列数据不能重复
  • 表中每一列的数据都依赖于这张表的主键
  • 表中除了逐渐以外的其他列,互相之间不能有直接依赖关系
3. 设计 redux 的 State 原则
  • 数据按照领域把整个应用的状态按照领域分成若干子 State,子 State 之间不能保存重复的数据
  • 表中 State 以键值对的解构存储数据,以纪录的 key/id 作为纪录的索引 ,纪录中的其他字段都依赖于索引
  • State 中不能保存可以通过已有数据计算而来的数据,即 State中的字段不相互依赖。
  • State 应该尽量扁平化(避免嵌套层级过深)
1.1.6.3 Selector 函数
// selector.js
export const getText = state=>state.text
// 组件中
import selector from './selectors'

const mapStateToProps = state = ({
  text: getText(state)
})
  • selector 还可以将拿到的 state 进行计算在返回
1.1.6.4 前端状态管理思想
  • 状态管理对接口进行二次封装处理

![1554164720007](F:\我的学习\My Study\07-React\assets\1554164720007.png)

  • 软件架构演变
  • 中台层是后端将微服务集合,也就是将小的API聚合为大的中台层

![1554164802253](F:\我的学习\My Study\07-React\assets\1554164802253.png)

1.1.6.5 Middleware

![1554165066437](F:\我的学习\My Study\07-React\assets\1554165066437.png)

// logger.js logger中间件
export default const logger = ({getState, dispatch}) = next => action = {
  console.log(action)
  console.log('next state', getState)
	const result = next(action)
  return result
}
  • 使用
// index.js 中
import logger from "./redux/logger";

const store = createStore(Reducer, applyMiddleware(thunk,logger))
1.1.6.6 Store enhancer
1. 一般结构
function enhancerCreator (){
  return createStore => (...args) =>{
    
  }
}

1.1.1 Fragment 占位符

  • Fragment 占位符并不会被渲染成任何标签元素
import {Fragment} from 'react';
class App extends Component {
    render (){
        return (
        	<Fragment>
            	<div></div>
            	<div></div>
            </Fragment>
        )
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaodingyang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值