react狀態管理了解一下

redux解讀

重點在源碼部分,用法能夠翻閱文檔或者googlereact

// 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模擬實現 回顧一下幾個重點內容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裏面去改,爲了解藕,須要將業務邏輯抽離出來app

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源碼中解讀訂閱事件async

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. 爲何要用他或者說他解決了什麼問題?ide

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,看看是怎麼解決異步問題的!ui

相關文章
相關標籤/搜索