# 状态管理器

# 简单的状态管理器

redux是一个状态管理器,那什么是状态呢?状态就是数据,比如计数器中的count。

const state = {
  count: 1
};

我们来使用下状态

console.log(state.count);

我们来修改下状态

state.count = 2;

好了,现在我们实现了状态(计数)的修改和使用了。

读者:你当我傻吗?你说的这个谁不知道?

笔者:哎哎哎,别打我!redux核心就是这个呀!我们一步步扩展开来嘛!

当然上面有一个很明显的问题:修改count之后,使用count的地方不能收到通知。

我们可以使用发布-订阅模式来解决这个问题。

// count 的发布订阅者实践
const state = {
  count: 1
};
const listeners = [];

// 订阅
function subscribe(listener) {
	listeners.push(listener); 
}

function changeCount(count) {
  state.count = count;
  // 当 count 改变的时候,我们要去通知所有的订阅者
  for (let i = 0; i < listeners.length; i++) {
    const listener = listeners[i];
    listener();
  }
}

我们来尝试使用下这个简单的计数状态管理器。

// 来订阅一下,当 count 改变的时候,我要实时输出新的值
subscribe(() => {
  console.log(state.count);
});

// 我们来修改下 state,当然我们不能直接去改 state 了,我们要通过 changeCount 来修改
changeCount(2);
changeCount(3);
changeCount(4);

现在我们可以看到,我们在修改count的时候,会输出相应的count值。

现在有两个新的问题摆在我们面前

  • 这个状态管理器只能管理count,不通用
  • 公共的代码要封装起来

我们尝试来解决这个问题,把公共的代码封装起来

const createStore = function (initState) {
  const state = initState;
  const listeners = [];
  
  // 订阅
  function subscribe(listener) {
    listeners.push(listener);
  }
  
  function changeState(newState) {
    state = newState;
    // 通知
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i];
      listener();
    }
  }
  
  function getState() {
    return state;
  }
  
  return {
    subscribe,
    changeState,
    getState
  }
}

我们来使用这个状态管理器管理多个状态counter和info试试

const initState = {
  counter: {
    count: 0
  },
  info: {
  	name: '',
  	description: ''
	}
};

const store = createStore(initState);

store.subscribe(() => {
  const state = store.getState();
  console.log(`${state.info.name}: ${state.info.description}`);
};
store.subscribe(() => {
  const state = store.getState();
  console.log(state.counter.count);
});

store.changeState({
  ...store.getState(),
  info: {
    name: '前端九部',
    description: '我们都是前端爱好者!'
  }
});

store.changeState({
  ...store.getState(),
  counter: {
    count: 1
  }
});

到这里我们完成了一个简单的状态管理器。

这里需要理解的是createStore,提供了changeStategetStatesubscribe三个能力。

本小节完整源码见demo-1 (opens new window)

# 有计划的状态管理器

我们用上面的状态管理器来实现一个自增,自减的计数器。

const initState = {
  count: 0
};
const store = createStore(initState);

store.subscribe(() => {
  const state = store.getState();
  console.log(state.count);
});
// 自增
store.changeState({
  count: store.getState().count + 1
});
// 自减
store.changeState({
  count: store.getState().count - 1
});
// 不符合规则的随便修改
store.changeState({
  count: 'abc'
});

你一定发现了问题,count被改成了字符串abc,因为我们对count的修改没有任何约束,任何地方、任何人都可以修改。

我们需要约束,不允许计划外的count修改,我们只允许count自增和自减两种改变方式!

那我们分两步来解决这个问题

  1. 制定一个state修改计划,告诉store,我的修改计划是什么。

  2. 修改store.changeState方法,告诉它修改state的时候,按照我们的计划修改。

我们来设置一个plan函数,接受现在的state和一个action,返回经过改变后的新的state。

// 注意: action = {type: '', other: ''}, action 必须有一个 type 属性
function plan(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return {
        ...state,
        count: state.count + 1
      }
    case 'DECREMENT':
      return {
        ...state,
        count: state.count - 1
      }
    default:
      return state;
  }
}

我们把这个计划告诉store,store.changeState以后改变state要按照我的计划来改。

// 增加一个参数 plan
const createStore = function (plan, initState) {
  const state = initState;
  const listeners = [];
  
  function subscribe(listener) {
    listeners.push(listener);
  }
  
  function changeState(action) {
    // 请按照我的计划修改 state
    state = plan(state, action);
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i];
      listener();
    }
  }
  
  function getState() {
    return state;
  }
  
  return {
    subscribe,
    changeState,
    getState
  }
}

我们尝试使用下新的createStore来实现自增和自减

const initState = {
  count: 0
};

// 传入 plan 函数
const store = createStore(plan, initState);

store.subscribe(() => {
  const state = store.getState();
  console.log(state.count);
});

// 自增
store.changeState({
  type: 'INCREMENT'
});
// 自减
store.changeState({
	type: 'DECREMENT'
});
// 我想随便修改,但是计划外的修改是无效的
store.changeState({
  count: 'abc'
});

到这里为止,我们已经实现了一个有计划的状态管理器。

我们商量一下吧?我们把plan和changeState改一下名字好不好?

plan改成reducer,changeState改成dispatch!

不管你同不同意,我都要换,因为新名字比较厉害(其实因为redux是这么叫的)!

本小节完整源码见demo-2 (opens new window)

最近更新时间: 2020/5/26 19:03:58