fullstackReact 学习笔记 flux和redux

一、为什么使用flux?

因为react中将所有的state都保存在最顶部的组件中,然后通过props传递到下面各个组件中,下面各个组件的action将会通过回调函数层层传递到最顶层,最顶层组件的处理函数负责处理下面各个组件的action,并更新state。由于,复杂的项目一个action可能要更新很多种state,由此造成了顶部处理函数的庞大、集中,由此导致容易出错。

是否可以将state的更新分开管理,因此产生了flux.

flux其实是一种设计模式:

action=>dispatcher=>store=>viewer

就是:不在是action直接更新state进而更新viewer,而是在中间增加了一层store,store负责管理所有的state,当action需要更新state时候,将action通过dispatcher传递给store,store负责更新state,进而刷新viewer.

flux的好处:

(1)将state的管理独立出来,减轻了顶部组件的责任。

(2)react component将变大的更加的简化,由于component对应state的管理全部在外部store中进行,由此可以将react component设计为简单的render() html标签的函数。

(3)不用再匹配state tree和dom tree。如:你想在dom中显示一个人性化的timeStamp,其实state存储成一个时间点是最好的,但是如果state在react component中管理的话,就必须匹配state到dom中,这其中就必然将转换函数包含在组件中,以保持dom和state的一致性。

二、redux:flux的一种好的实践方法

redux其实很简单,核心代码不到100行。redux的核心观念:

(1)所有的state全部在store中存储,应用需要state时,需要通过store.getState()得到当前的全局的state.

(2)state在store外部是不会发生变动的。views需要将修改state的action发送到store中dispatch(action)=》store,然后store调用reducer以生成新的全局的state.

在store内部:

newstate=reducer(oldstate,action)

a、action来自view传递(dispatch)的一个对象{type:' '},必须包含一个type动作,便于在reducer中分类处理。

b、reducer接受上一次的state和viewer传递的action生成一个新的state

 

oldstate是上一次的state,如果没有处理过则为初始化的state.

newState生成后通过回调用所有的监听函数,让所有监听的组件(subscribe)进行更新。

示例:counter

aciton:

const incrementAction = { type: 'INCREMENT' };
const decrementAction = { type: 'DECREMENT' };
const unknownAction = { type: 'UNKNOWN' };

 action中还可以附带其他的参数,如制定每次增减的数量

const incrementAction = {
  type: 'INCREMENT',
  amount: 5,
};

  reducer:

function reducer(state, action) {
  if (action.type === 'INCREMENT') {
    return state + 1;
  } else if (action.type === 'DECREMENT') {
    return state - 1;
  } else {
    return state;
  }
}

  reducer中处理action中附带的参数:

function reducer(state, action) {
  if (action.type === 'INCREMENT') {
    return state + action.amount;
  } else if (action.type === 'DECREMENT') {
    return state - action.amount;
  } else {
    return state;
  }
}

 三、模拟store

store负责维护state,接受action,并调用reducer生成新的state.

它至少要提供两个功能:

getState()获取state.

dispatch()接受action

我们通过createStore来创建store

示例:

function createStore(reducer) {
  let state = 0;

  const getState = () => (state);

  const dispatch = (action) => {
    state = reducer(state, action);
  };

  return {
    getState,
    dispatch,
  };
}

  createStore接受reducer,返回两个功能getState和dispatch

使用createStore重写counter:

const store = createStore(reducer);

const incrementAction = {
  type: 'INCREMENT',
  amount: 3,
};

store.dispatch(incrementAction);
console.log(store.getState()); 

 dispatch仅仅发送动作给reducer,他不负责返回处理结果,处理后的state将通过getState接口返回。

工厂函数:

因为state在cerateStore中声明的私有变量,要保持state只有一份,以便于所有的调用都更新一个state,因此,store只能有一个,只能调用一次createStore

注意:reducer必须是纯函数

四、示例:构造一个chat app

1、state

const initialState = { messages: [] };

  state是包含一组信息的对象

2、actions

增加信息:

const addMessageAction1 = {
  type: 'ADD_MESSAGE',
  message: 'How does it look, Neil?',
};

  

删除信息:

const deleteMessageAction = {
  type: 'DELETE_MESSAGE',
  index: 0,
};

  

3、reducer

function reducer(state, action) {
  if (action.type === 'ADD_MESSAGE') {
    return {
      messages: state.messages.concat(action.message),
    };
  } else if (action.type === 'DELETE_MESSAGE') {
    return {
      messages: [
        ...state.messages.slice(0, action.index),
        ...state.messages.slice(
          action.index + 1, state.messages.length
        ),
      ],
    };
  } else {
    return state;
  }
}

  

4、createStore,

接受一个初始化的state

function createStore(reducer, initialState) {
  let state = initialState;

  const getState = () => (state);

  const dispatch = (action) => {
    state = reducer(state, action);
  };

  return {
    getState,
    dispatch,
  };
}

  

5、store

const store = createStore(reducer, initialState);

  

6、使用store提供的功能dispatch和getState

store.dispatch(addMessageAction1);
const stateV1 = store.getState();

  

7、component订阅更新

由于我们在react component 之外更新了state,因此无法像在react component 内部一样使用setState直接更新。因此我们这里使用观察着模式来更新组件。

观察者模式:

(1)在store内部创建一个观察者列表:listeners:[]

(2)对于需要更新state的component,可以调用一个subscribe()将listener添加到store中的listeners.

(3)每次完成新的state的计算,即newState=reducer(oldstate,action)之后,遍历观察者列表中的listener,并执行。这样所有的观察则组件便完成了更新

store内部实现代码:

function createStore(reducer, initialState) {
  let state = initialState;
  const listeners = [];
  // ...

  const subscribe = (listener) => (
    listeners.push(listener)
  );

  const getState = () => (state);

  // ...
  const dispatch = (action) => {
    state = reducer(state, action);
    listeners.forEach(l => l());
  };
  // ...

  // ...
  return {
    subscribe,
    getState,
    dispatch,
  };
}

  使用listener

const store = createStore(reducer, initialState);

const listener = () => {
  console.log('Current state: ');
  console.log(store.getState());
};

store.subscribe(listener);

const addMessageAction1 = {
  type: 'ADD_MESSAGE',
  message: 'How do you read?',
};
store.dispatch(addMessageAction1);

  这样每次传递完action之后,便完成了自动的控制台输出。

8、将react 和redux联系起来

(1)使用store.getState()获取state,而不是在最顶层的component中管理state

(2)使用store.subscribe()订阅更新,(使用生命周期函数componentDidMount()中强制更新(forceUpdate)),而不再使用setState去手动更新。

(3)使用store.dispatch()传递action,而不再需要通过回调函数,回调最顶层组件的处理函数处理数据了。

class App extends React.Component {
  componentDidMount() {
    store.subscribe(() => this.forceUpdate());
  }

  render() {
    const messages = store.getState().messages;

    return (
      <div className='ui segment'>
        <MessageView messages={messages} />
        <MessageInput />
      </div>
    );
  }
}

class MessageInput extends React.Component {
  state = {
    value: '',
  };

  onChange = (e) => {
    this.setState({
      value: e.target.value,
    })
  };

  handleSubmit = () => {
    store.dispatch({
      type: 'ADD_MESSAGE',
      message: this.state.value,
    });
    this.setState({
      value: '',
    });
  };

  render() {
    return (
      <div className='ui input'>
        <input
          onChange={this.onChange}
          value={this.state.value}
          type='text'
        />
        <button
          onClick={this.handleSubmit}
          className='ui primary button'
          type='submit'
        >
          Submit
        </button>
       </div>
    );
  }
}

class MessageView extends React.Component {
  handleClick = (index) => {
    store.dispatch({
      type: 'DELETE_MESSAGE',
      index: index,
    });
  };

  render() {
    const messages = this.props.messages.map((message, index) => (
      <div
        className='comment'
        key={index}
        onClick={() => this.handleClick(index)}
      >
        {message}
      </div>
    ));
    return (
      <div className='ui comments'>
        {messages}
      </div>
    );
  }
}

  注意:我们在存在input的组件中保留了state,这是一个通用的实践,便于更新state.

 

posted @ 2018-08-17 06:47  tutu_python  阅读(136)  评论(0)    收藏  举报