一个新的状态管理类库 unstated.js:简单易用/合理

#1

先讲讲这个:一个简单的输入框,我们使用 this.state 就好了,但是这个输入框输入的数据影响界面上多个地方时,我们需要将输入框抽离出来,抽到更上一层通过props进行分发(redux),或者通过订阅观察者机制进行分发(flux),从性能考虑,flux是最好的,需要的界面组件进行订阅自己需要的数据,但是比较繁琐;

新出了一个基于 react/preact 的数据管理类库 unstated 打破了这个规则:

只有三个 API,简单易用;和之前的 state 管理库思路完全不同,这个unstated主打 local state(不是全局store,一个小改动导致整棵树 rerender),多个 local state 之间也可以共享, 兼具了redux的易用性与flux的合理性,令人耳目一新;

什么是 local state: 就是组件内的state;

也兼容 preact!

下面我们来从下面的 todoList 来了解一下这个到底多么好用;

React + redux 多组件初始化以及更新问题请教
#2

首先我们来组织一下我们的 Container

这个 Container 类似 redux 的 store,但是比 redux的store 简单易懂,包含了数据和操作数据的方法

import React from 'react';
import { Provider, Container, Subscribe,  } from 'unstated';

// todo list 数据,和操作该数据的方法
class TodoContainer extends Container {
  state = {
    todos: []
  }

  add(text) {

  }

  toggle(id) {

  }

  remove(id) {

  }
}

// 输入框数据
class TodoInputContainer extends Container {
  state = {
    text: ''
  }

  change(text) {
    this.setState(text)
  }
}

// 过滤搜索数据
class TodoFilterContainer extends Container {
  state = {
    keyword: '',
    status: ['all', 'removed', 'finish'],
    current_status: 'all',
  }

  changeStatus(current_status) {
    this.setState({current_status})
  }

  changeKeyword(keyword) {
    this.setState({keyword})
  }

}

没有redux的reducer/actions/中间件等这些多余的概念…

现在该将Container绑定到我们需要的地方了:Subscribe,是一个组件的写法:

...

// 这里我们只渲染一个简单的 TodoInputContainer 
function TodoInput(props) {
    <Subscribe to=[TodoInputContainer]>
        {
           todoInput => (
                <input 
                     value={todoInput.state.text} 
                     onChange={e => todoInput.change(e.target.value)} 
                /> 
           )
        }
    </Subscribe>
} 

这里只是写一个引子,多个 Container 怎么共享? 看看官方例子(clone 下来,npm install
&& npm run example): https://github.com/thejameskyle/unstated/tree/master/example

上面的只是本地数据交互的,接下来我们谈谈在 Unstated 里面怎么做请求数据交互;

#3

如何在 unstated 里面做初始化的数据请求?

unstated 里面做请求是很简单的事情,但因为 Subscribe 组件只接受 tochildren 两个props,所以异步请求的数据怎么初始化?

unstated作者的 demo 写的包含了基础用法和多个state共享的用法,但是没有考虑到数据请求;用他现存的 API 做第一次初始化数据请求时,完全没有头绪。 现在能想到的是再写一个类似这样的组件:

class FetchContainer extends Component {
  componentDidMount() {
    this.props.onInit && this.props.onInit();
  }

  componentWillUnmount() {
    this.porps.onDestroy && this.porps.onDestroy()
  }
  
  render() {
    return this.props.children
  }
}

这样用:

...
function Counter() {
  return (
    <Subscribe to={[CounterContainer]}>
      {counter => (
        <FetchContainer onInit={() => {
          counter.increment();
        }} onDestroy={() => {
            counter.decrement();
        }}>
          <div>
            <button onClick={() => counter.decrement()}>-</button>
            <span>{counter.state.count}</span>
            <button onClick={() => counter.increment()}>+</button>
          </div>
        </FetchContainer>
      )}
    </Subscribe>
  );
}
1 Like