redux学习理解05

redux-study

Posted by EWL on December 14, 2018

react-redux的学习实现第一课:将react和redux结合起来

首先,利用create-react-app创建一个新的项目:


|__public__ index.html
|__src__ index.js
     |__ Header.js
     |__ Content.js
     |__ ThemeSwitch.js

index.js

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import './index.css';
import Header from './Header';
import Content from './Content';

// 创建createStore
function createStore(reducer) {
  let state = null;
  let listeners = [];

  const getState = () => state;//初始化/获取state的函数

  // 订阅事件
  const subscribe = (listener) => {
    listeners.push(listener);
  };

  const dispatch = (action) => {
    state = reducer(state, action);
    //每一次在执行完stateChanger之后就将事件队列里的监听事件执行处罚
    listeners.forEach((listener) => listener());
  };
  dispatch({});// 初始化一次,保证getState能够将初始化的state返回

  return { getState, dispatch, subscribe };
}

// 创建reducer
function themeReducer(state, action) {
  if(!state) {
    return {
      themeColor: 'red'
    };
  }

  switch (action.type) {
    case 'CHANGE_COLOR':
      return {
        ...state,
        themeColor: action.themeColor
      };
    default:
      return state
  }
  
}

const store = createStore(themeReducer);

class Index extends Component {

  static childContextTypes = {
    store: PropTypes.object
  };

  getChildContext() {
    return { store };
  }

  render() {
    return (
      <div>
        <Header />
        <Content />
      </div>
    );
  }
}

ReactDOM.render(<Index />, document.getElementById('root'));

Header.js

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Header extends Component {
  constructor(props) {
    super(props);
    this.state = {
      themeColor: ''
    };
  }

  static contextTypes = {
    store: PropTypes.object
  };

  componentWillMount() {
    this._updateThemeColor();
  }

  _updateThemeColor = () => {
    const { store } = this.context;
    const state = store.getState();

    this.setState({
      themeColor: state.themeColor
    });
  };

  render() {
    return (
      <h1 style=>我是Title</h1>
    )
  }
}

export default Header;

Content.js

import React, { Component } from 'react';
import ThemeSwitch from './ThemeSwitch';
import PropTypes from "prop-types";

class Content extends Component {
  constructor(props) {
    super(props);
    this.state = {
      themeColor: ''
    };
  }

  static contextTypes = {
    store: PropTypes.object
  };

  componentWillMount() {
    this._updateThemeColor();
  }

  _updateThemeColor = () => {
    const { store } = this.context;
    const state = store.getState();

    this.setState({
      themeColor: state.themeColor
    });
  };

  render() {
    return (
      <div>
        <p style=>我是Content</p>
        <ThemeSwitch />
      </div>
    )
  }
}

export default Content;

ThemeSwitch.js

import React, { Component } from 'react';
import PropTypes from "prop-types";

class ThemeSwitch extends Component {
  constructor(props) {
    super(props);
    this.state = {
      themeColor: ''
    };
  }
  static contextTypes = {
    store: PropTypes.object
  };

  componentWillMount() {
    this._updateThemeColor();
  }

  _updateThemeColor = () => {
    const { store } = this.context;
    const state = store.getState();

    this.setState({
      themeColor: state.themeColor
    });
  };


  render() {
    return (
      <div>
        <button style=>red</button>
        <button style=>blue</button>
      </div>
    )
  }
}

export default ThemeSwitch;

上述代码仅仅完成了store在context上的挂载,但是还没有正式开始设置颜色切换事件。 划重点:

  1. 本节的store是使用context去挂载的,所以在定点的父组件Index需要设置childContextTypes,否则一定报错
  2. 同理,子组件的contextTypes也必须要设置,否则一定报错
  3. 可以注意到在Index中我们初始化store的时候,我们需要将dispatch先执行一次,否则我们是没有初始化的state可用的

接下来,我们就开始在ThemeSwitch中设置颜色切换函数

handleSwitchColor = (color) => {
    const { store } = this.context;
    store.dispatch({
      type: 'CHANGE_COLOR',
      themeColor: color
    });
  };

但是这一段代码仍然是没有反应的,我们在Index组件中的createStore函数里面打断点看看最新的state是否更新了,如下:

function createStore(reducer) {
  let state = null;
  let listeners = [];

  const getState = () => state;//初始化/获取state的函数

  // 订阅事件
  const subscribe = (listener) => {
    listeners.push(listener);
  };

  const dispatch = (action) => {
    state = reducer(state, action);
    console.log('the newest state', state);
    //每一次在执行完stateChanger之后就将事件队列里的监听事件执行处罚
    listeners.forEach((listener) => listener());
  };
  dispatch({});// 初始化一次,保证getState能够将初始化的state返回

  return { getState, dispatch, subscribe };
}

先看一下初始化的the newest state image.png

然后点击’blue’按钮⤵️

image.png

这说明我们切换的时候,已经成功将state修改了,看到dispatch中的listeners的时候,突然想起来,我们每一次在调用dispatch之后都需要去将修改后触发的函数推入订阅事件的队列里。现在我们开始添加订阅事件:

  1. 每一次修改state之后触发的函数是updateThemeColor
  2. 每一次调用updateThemeColor的时候,都需要从store中直接获取已经最新的state,然后调用setState去更新各个组件中的颜色

明确以上两点后,我们可以开始添加代码了。

componentWillMount() {
    const { store } = this.context;
    this._updateThemeColor();
    store.subscribe(this._updateThemeColor);
  }

将上述代码分别添加进Header/Content/ThemeSwitch中,然后刷新页面如下 初始化状态 image.png

点击blue按钮 image.png

本节学习总结:

  1. 每一次调用dispatch之后都需要将对应的修改状态后的函数推入事件队列,即订阅
  2. 修改之后,store中会保存着最新的state,各个组件只需要通过store.getState即可获得最新的state