新的React狀態庫:foca

原罪發表於2021-12-23

基於 redux 和 react-redux。
倉庫地址:github.com/foca-js/foca
文件地址:foca.js.org

理念

TS First,無TS不程式設計!

特性

  • 模組化開發
  • 專注 typescript 極致體驗
  • 模型自動註冊,匯出即可使用
  • 內建 immer 快速處理資料
  • 智慧追蹤非同步函式的執行狀態
  • 模型支援私有方法
  • 可定製的多引擎資料持久化
  • 資料隔離,允許同類狀態庫並存

架構圖

image.png

線上試玩

CodeSandBox

使用

定義模型

// File: counterModel.ts
import { defineModel } from 'foca';

const initialState: { count: number } = {
  count: 0,
};

// 無須手動註冊到store,直接匯出到react元件中使用
export const counterModel = defineModel('counter', {
  // 初始值,必填屬性,其他屬性均可選
  initialState,
  actions: {
    // state可自動提示型別 { count: number }
    plus(state, value: number, double: boolean = false) {
      // 直接修改狀態
      state.count += value * (double ? 2 : 1);
    },
    minus(state, value: number) {
      // 直接返回新狀態
      return { count: state.count - value };
    },
    // 私有方法,只能在模型內部被effect方法呼叫,外部呼叫則TS報錯(屬性不存在)
    _clear(state) {
      return this.initialState;
    },
  },
  effects: {
    // 非同步函式,自動追蹤執行狀態(loading)
    async doSomething() {
      // 呼叫私有方法
      await this._sleep(100);

      // 快速處理狀態,對於網路請求的資料十分方便
      this.setState({ count: 1 });
      this.setState((state) => {
        state.count += 1;
      });
      // 呼叫action函式處理狀態
      this.plus(1, true);

      // 呼叫effect函式
      return this.commonUtil(1);
    },
    // 普通函式
    commonUtil(x: number) {
      return x + 1;
    },
    // 私有方法,只能在模型內部使用,外部呼叫則TS報錯(屬性不存在)
    _sleep(duration: number) {
      return new Promise((resolve) => {
        setTimeout(resolve, duration);
      });
    },
  },
  hooks: {
    // store初始化完成後觸發onInit鉤子
    onInit() {
      this.plus(1);
      console.log(this.state);
    },
  },
});

在函式元件中使用

import { FC, useEffect } from 'react';
import { useModel, useLoading } from 'foca';
import { counterModel } from './counterModel';

const App: FC = () => {
  // count型別自動提示 number
  const { count } = useModel(counterModel);
  // 僅effects的非同步函式能作為引數傳入,其他函式TS自動報錯
  const loading = useLoading(counterModel.doSomething);

  useEffect(() => {
    counterModel.doSomething();
  }, []);

  return (
    <div onClick={() => counterModel.plus(1)}>
      {count} {loading ? 'Loading...' : null}
    </div>
  );
};

export default App;

在類元件中使用

import { Component } from 'react';
import { connect, getLoading } from 'foca';
import { counterModel } from './counterModel';

type Props = ReturnType<typeof mapStateToProps>;

class App extends Component<Props> {
  componentDidMount() {
    counterModel.doSomething();
  }

  render() {
    const { count, loading } = this.props;

    return (
      <div onClick={() => counterModel.plus(1)}>
        {count} {loading ? 'Loading...' : null}
      </div>
    );
  }
};

const mapStateToProps = () => {
  return {
    count: counterModel.state.count,
    loading: getLoading(counterModel.doSomething);
  };
}

export default connect(mapStateToProps)(App);

希望能成為你下一個專案的狀態管理方案!喜歡就先star一下吧。
倉庫地址:https://github.com/foca-js/foca

相關文章