react状态管理了解一下

redux解读

重点在源码部分,用法可以翻阅文档或者google

// redux 状态管理 把数据集中存放
// 当前所有组件的状态

function createStore(reducer) {
  let state;
  let listeners = []
  let getState = ()=> state; // 把对象克隆一份
  let dispatch = (action)=>{
    state = reducer(state,action);
    listeners.forEach(fn=>fn());
  }
  let subscribe = (fn)=>{
    listeners.push(fn);
    return ()=>{
      listeners = listeners.filter(l=>fn !== l);
    }
  }
  dispatch({type:'@INIT'})
  return {
    subscribe,
    getState,
    dispatch
  }
}

// 1.redux 中不能直接操作状态
// 2.如果任意一个组件中想更新状态,需要派发一个动作
// 3.每次更新状态 最好用一个新的状态对像覆盖掉 (时间旅行)
let initState  = { // 状态已经抽离出去了不能直接更改
  title: { content: '你好', color: 'red' },
  content: { content: '哈哈', color: 'green' }
}
function reducer(state = initState,action){ // reducer的参数 有两个第一个就是用户派发的动作,第二个就是当前组件的状态
  switch (action.type) {
    case 'CHANGE_TITLE_COLOR':
      return {...state,title:{...state.title,color:action.color}}
    case 'CHANGE_CONTENT_CONTENT':
      return {...state,content:{...state.content,content:action.content}}
  }
  return state
}
let store = createStore(reducer);
store.subscribe(renderApp);   //dispatch的时候 就会触发renderApp
let unsub = store.subscribe(()=>console.log('呵呵 更新了'))
setTimeout(() => {
  // 实现状态更新的方法
  store.dispatch({type:'CHANGE_TITLE_COLOR',color:'pink'});
}, 1000);
setTimeout(() => {
  unsub();
  store.dispatch({ type: 'CHANGE_CONTENT_CONTENT', content: '新的内容' });
}, 2000);
function renderTitle(){
  let title = document.getElementById('title');
  title.innerHTML = store.getState().title.content;
  title.style.background = store.getState().title.color;
}
function renderContent() {
  let content = document.getElementById('content');
  content.innerHTML = store.getState().content.content;
  content.style.background = store.getState().content.color;
}
function renderApp(){
  renderTitle();
  renderContent();
}
renderApp();
复制代码

看完上面的简易redux模拟实现 回顾一下几个重点内容

  1. redux中不能直接修改状态是怎么做到的?getState源码
getState = () => {     //这样之后我们通过getState获取的实际就是state的克隆,直接改就不会影响到state
  // return state   redux源码其实是这样的 并没有像下面这样包一层,所以我们看到可以改变数据但是会有报错提示
  return JSON.parse(JSON.stringfy(state))
}
复制代码
  1. 接着上一个问题,如果不能直接修改状态,那怎么去修改呢? dispatch
dispatch({
  type: '',
  payload: ''
})
复制代码
  1. reducer

在没有reducer之前 我们要想更改状态都是在createStore里面去改,为了解藕,需要将业务逻辑抽离出来

function createStore(state, reducer) {
  function getState() {

  },
  function dispatch(action) {
    //dispatch的结果是告诉reducer要改什么
    state = reducer(state, action)
  }
  return {
    dispatch,
    getState
  }
}

function reducer(state, action) {

}
复制代码
  1. 如何解决每次更改状态都要重新执行renderApp()的问题 --- 》 发布订阅

  2. 在redux源码中解读订阅事件


function dispatch(action) {
    <!--if (!isPlainObject(action)) {-->
    <!--  throw new Error(-->
    <!--    'Actions must be plain objects. ' +-->
    <!--      'Use custom middleware for async actions.'-->
    <!--  )-->
    <!--}-->
    <!--if (typeof action.type === 'undefined') {-->
    <!--  throw new Error(-->
    <!--    'Actions may not have an undefined "type" property. ' +-->
    <!--      'Have you misspelled a constant?'-->
    <!--  )-->
    <!--}-->
    <!--if (isDispatching) {-->
    <!--  throw new Error('Reducers may not dispatch actions.')-->
    <!--}-->

    try {
      isDispatching = true
      <!--currentReducer就是当前正在执行的reducer-->
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()   //执行所有的订阅事件 往往是 this.setState({}) reactsetState会比较前后的两个值 如果值变化了才会重新渲染 所以不用担心 所以组件都订阅后会全部this.setState导致不需要的视图刷新
    }

    return action
  }
  <!--我们看到 每个组件要是想用redux都需要订阅 这个很麻烦 react-redux就是为了解决这个问题的-->
复制代码

react-redux解读

我们在学习一个新东西的时候始终要想那么几个问题:1. 怎么用?2. 为什么要用他或者说他解决了什么问题?

react-redux基本用法

// store全局注入
import {Provider} from 'react-redux'
ReactDOM.render(<Provider store={store}>
<>
  <Counter></Counter>
  <Todo></Todo>
  <Test><p>xxxx</p><p>xxxx</p></Test>
</>
</Provider>,window.root);
复制代码
//  actions/connter.js
import * as Types from '../action-types';
// action 
export default {
  add(val){
    return {type:Types.INCREMENT,count:val}
  }
}
复制代码
//组件内部使用
import React from 'react';
import actions from '../store/actions/counter';
import { connect } from 'react-redux';
class Counter extends React.Component {
  
  handleClick = () => {
    this.props.add(3);   // connect传过来的
  }
  render() {
    return (
      <>
        <p>{this.props.number}</p>
        <button onClick={this.handleClick}>点击增加</button>
      </>
    )
  }
}
export default connect(
  (state) => ({ ...state.counter }),   //返回的都可以通过this.props.xxx获取到
  actions
)(Counter);

复制代码

为了解决每次都需要在组件中将属性绑定到状态,还要订阅数据的问题 我们来看看react-redux是怎么解决的

部分源码解析

react中的provider

// context.js
import React from 'react';

let Context = React.createContext();


export  default Context;
复制代码
//provider源码
import React,{Component} from 'react';
import Context from './context';

// Provider 主要是提供store使用的
export default class Provider extends Component{
   render(){
     return (<Context.Provider value={{ store: this.props.store}}>
         {this.props.children}
     </Context.Provider>)
 }
}
复制代码
//connect源码
import React from 'react';
import Context from './context';
import { bindActionCreators} from 'redux'; 
let connect = (mapStateToProps, mapDipsatchToProps)=> (Component)=>{
  return ()=>{
    class Proxy extends React.Component{
      state = mapStateToProps(this.props.store.getState());   //将redux中是数据放到state身上,
      //当redux中的数据变化时-- 》 此state变化 --》 此组件刷新 --》 儿子组件(Component)刷新  
      //这样就做到了 当redux数据变化时,组件会自动刷新而不需要再每个组件中订阅了
      componentDidMount(){
        this.unsub = this.props.store.subscribe(()=>{
          this.setState(mapStateToProps(this.props.store.getState()))
        })
      }
      componentWillUnmount(){
        this.unsub();
      }
      render(){
        let mapDispatch;   //将dispatch传到作为store的action参数 dispatch  
        /**{
          add: function() {
            return dispatch(add.apply(this, arguments))
          },
          minis: function() {
            return dispatch(minis.apply(this, arguments))
          }
        }*/
        if (typeof mapDipsatchToProps === 'object'){ //如果第二个参数传递的是一个对象,把对象直接进行包装即可
          mapDispatch = bindActionCreators(mapDipsatchToProps,this.props.store.dispatch);
        }else{
          mapDispatch = mapDipsatchToProps(this.props.store.dispatch);   
        }
        return <Component {...this.state} {...mapDispatch}></Component>
      }
    }
    return <Context.Consumer>
      {({store})=>{
        // 将状态 和dispatch 拿到执行函数 把结果对象传递给原本的component渲染
        return <Proxy store={store}></Proxy>
      }}
    </Context.Consumer>
  }
}
export default connect;
复制代码

以上就是我个人对redux的学习和理解,分享给大家,当然还有很多不足之处希望大家指出,这里reducer都是纯函数,无法处理异步问题,那在业务代码中肯定是少不了异步数据的请求的,怎么解决呢?下一节开始学习redux-saga,看看是怎么解决异步问题的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值