HooX:Reactの次世代ステータス管理ツールではないはず


前言


実は1ヶ月以上前に、私もすでに掘金発hooxの紹介をしたことがあります.しかし、私はこのような簡単なものも、あまり技術的な含有量がないと思います.つまり、勝手に髪を出して見て、楽しんでいます.最近、Hookベースのステータスマネージャが増えていることに気づきました.それでは、後でパクリにならないように、私もここで集まってみました.
また、今の私のこのおもちゃは、まだ0です.xのバージョン.私はまだ正式版を出す勇気がありません.一方で、私自身がまだ完璧ではないと思っています.一方、それは確かに多くのプロジェクトの試練を経ていない.しかし、業務流量だけで言えば、アリ保険の数百万~千万UV級のC端ページを長い間走っていた.現在のところ、明らかな異常はない.これも私が文章を出したときに少しでも持っていた底気です.
本文に戻る:

どうしてまた車輪を作るの?


hookホイールリング付き


react hookについてはあまり紹介しません.hookは抽象的な状態を提供する能力を提供し,自然にhookに基づいてグローバルな状態から引き離すことができると考えられる.生まれつき車輪の輪を持っているので、コミュニティにもhookベースの状態管理ツールがたくさん現れています.例えば、先日の飛氷チームが出したicestoreや、このstamenですが、私がもっと好きなのはこのunstated-nextです.
他の人がそんなにたくさんの車輪を作った以上、どうして自分で作ったのですか.理由は次のとおりです.

他人の車輪が足りない


例えばunstated-nextは、本質的にカスタムhookをグローバル化しています.理念はとても良くて、状態の論理が比較的に複雑な話で、書くのが少し疲れます.state、actions、effectsをカスタムhookに維持する必要があります.内部の一連のactions、effectsにはuseCallback、useMemoを追加する必要がありますが、外部に引き離すと多くのパラメータが伝わります.要するに、プロジェクトが比較的複雑であれば、書くのが疲れます.
stamenも実はいいですね.state、reducer、effectsを含むstoreを宣言します.また、コンポーネントにProviderを包む必要はなく、あちこちで勝手に挿入し、更新に応答します.dispatchはあまり好きではありませんが、actionやeffectの声明に直接位置決めするのはよくありません.そして、入参出参のタイプを失いました.
icestoreの問題も少なくない.TSをサポートしているというのは、実は欠けていて、ソースコードを見て、タイプが完全に失われました.また、ネーミングスペースというセットもあまり好きではありません.
また、先日アリ体験技術部の同級生からもhoxが出てきました.名前は私のこれに似ていますが、確かに一つのものではありません.それはunstated-nextとstatemenの結合体に似ています.私の理解では、その核心はunstated-nextに基づいて、ネストProviderの問題を解決したいことです.しかし、これは私がunstated-nextを使ったときの痛みではありません.また,その内部はReactDOM.renderで実現されており,SSRは実現できない.
もちろん、これらの問題は他の人も最適化することができます.しかし、何もしないで、もともとコードが何行もなくて、人にPRの时間をあげて、私は自分で車輪を書きました.だからとにかく、自分で作りましょう.

私の理想型


では、自分が望んでいるステータス管理ツールはどうですか?hooxの前に、実は私はまた1版を実現して、基本的にdvaのapiの1つのバージョンをコピーしました(yieldをasync/awaitに変えます).icestoreに似ていますが、ネーミングスペースはありません.しかし、icestoreはstamenと同じ問題を抱えており、action/effectの声明に直接位置づけることはできません.
その後、私が本当に望んでいるのはどんなものなのかをまとめました.
  • グローバルステータス管理ですが、単一storeではありません.
  • actionsとeffectsは正常な関数であり、独立して宣言し、直接参照する.
  • 完璧なTSサポート.

  • だから目標は簡単で、unstated-nextのhook小包版と言える.そこで私は1版を実現しました.最終的な効果は以下の通りです.

    HooX


    グローバル・ステータスの作成

    // store.js
    import createHoox from 'hooxjs'
    
    //         
    const state = {
      count: 1
    }
    
    //   store
    export const {
      Provider, //                ,   Provider  
      useHoox, //       ,           ,  useState
      getHoox //       ,  useHoox,         ,         ,   effect action 
    } = createHoox(state)
    
    //      action
    export const up = () => {
      const [{ count }, setHoox] = getHoox()
      return setHoox({ count: count + 1 })
    }
    
    //      effect
    export const effectUp = async () => {
      // getHoox   useHoox
      const [{ count }, setHoox] = getHoox()
      const newState = { count: count + 1 }
      await fetch('/api/up', newState)
      return setHoox(newState)
      //       action
      // return up()
    }

    もちろん、action/effectシーンが簡単なら、簡単なapiもあります.
    export const {
      // ...  api
      setHoox
    } = createHoox(state)
    
    //      action
    export const up = () => setHoox(({ count }) => ({ count: count + 1 }))

    このようにして、action/effectおよびグローバルステータスが作成され、hookから離脱することが分かる.次のようなメリットがあります.
  • action/effectはhookになく、renderによる関数の再宣言を避ける(さらにuseCallback/useMemoが必要).
  • は、方法を他のファイルから容易に抽出し、単一のファイルの複雑さを低減することができる.

  • 消費状態


    コンポーネントにグローバルステータスを使用するには、応答式を保証するためにuseHooxで取得する必要があります.action/effectを使用する場合は、比較的簡単で、直接引用すればよい.
    コンポーネントは、応答式の論理を持たないため、getHooxを介してグローバル状態を取得すべきではない.ステータスも取得できますが、ステータスの変更によってコンポーネントrenderがトリガーされるわけではありません.
    import { useHoox, up, effectUp } from './store'
    
    function Counter() {
      const [state] = useHoox()
      return (
        
    {state.count}
    ) }

    ステータスの直接変更


    シーンが単純で抽象的なactionを必要としない場合は、コンポーネント内で直接ステータスを更新することもできます.
    import { useHoox } from './store'
    
    function Counter() {
      const [state, setHoox] = useHoox()
      return (
        
    {state.count}
    setHoox({ count: event.target.value })} />
    ) }

    このコンポーネントがステータスのみを変更する場合は、消費ステータスを必要とせず、setHooxを直接使用することもできます.
    import { setHoox } from './store'
    
    function Inputer() {
      return (
        
    setHoox({ count: event.target.value })} />
    ) }

    ステータスのリセット


    classコンポーネントでは、this.setStateを介してステータスのマージ更新が行われていることを知っています.しかしfunctionコンポーネントでは、useStateが返す2番目のパラメータsetStateがまた置換更新される.実際の使用では、実は私たちはすべて訴えています.特にTS以外の項目では,状態モデルが動的である可能性があり,リセット状態を必要とする可能性が高い.すべての人のニーズを満たすために、私もapiを追加して皆さんが使いやすいようにしました.
    import { useHoox } from './store';
    
    function Counter() {
      const [state, setHoox, resetHoox] = useHoox()
      return (
        
    {state ?
    {state.count}
    : null}
    ) }

    グローバルcomputed


    上記apiにより、vue中のcomputedのような効果を実現することもできる.
    import { useHoox } from './store'
    
    export function useDoubleCount() {
      const [{ count }] = useHoox()
      return count * 2
    }

    いくつかの非常に複雑な演算については、reactのuseMemoを使用して最適化することもできます.
    import { useHoox } from './store'
    
    export function usePowCount(number = 2) {
      const [{ count }] = useHoox()
      return useMemo(() => Math.pow(count, number), [count, number])
    }

    また、いくつかのグローバルhooksも実現できます.

    connect


    実は普通、私のビジネスコードはほとんどconnectを書くことができません.直接useHooxでいいです.しかし、例外は2つあります.
  • は、connect propsによってグローバル状態論理をデカップリングしようとするいくつかの汎用的なコンポーネントを参照する.
  • 以前から書かれているclassコンポーネントはfunctionコンポーネントに改造したくないが、グローバル状態にも使用される.

  • したがってconnectというapiを実現し,この2つの問題を容易に解決した.
    まずstoreでこのapiを露出する必要があります.
    // store.js
    export const {
      // ...  api
      connect
    } = createHoox(state)

    次に、関数コンポーネントの場合:
    // Counter.js
    import { connect } from './store'
    
    const Counter = ({ count }) => {
      return 
    {count}
    } const NewCounter = connect(state => ({ count: state.count }))(Counter) export default NewCounter
    connect以降、返されるNewCounterは、countというpropを受け入れる必要がなくなり、これもすでにタイプ導出ができています.
    デコレーションを使いたいなら、関数コンポーネントは仕方ありませんが、classでいいです.
    import { connect } from './store'
    
    @connect(state => ({ count: state.count }))
    export default class Counter extends React.PureComponent {
      render() {
        return 
    {this.props.count}
    } }

    しかし、この装飾器はjs環境に限られ、ts環境ではclassの戻りタイプを変更することはできない.しかし、実際のコードでは、コンポーネントがconnectになった後、新しい関数コンポーネントを返し、コンポーネントPropsのタイプ(グローバル状態注入のpropsを除去)を変更します.従ってts環境では、装飾器を正常に使用できない.もちろん、関数小包を使用することはできます.
    import { connect } from './store'
    
    class Counter extends React.PureComponent {
      render() {
        return 
    {this.props.count}
    } } export default connect(state => ({ count: state.count }))(Counter)

    美しくないところ


    Providerが必要


    hoox下位層はcontextuseStateに基づいて実現され、contextの状態が存在するため、Reduxのように消費状態のコンポーネントは対応するContext.Providerの子孫コンポーネントでなければならない.次のようになります.
    import { Provider } from './store'
    import Counter from './counter'
    
    function App() {
      return (
        
          
        
      )
    }

    これにより、1つのコンポーネントが2つのstoreを消費する必要がある場合、2つのProviderの子孫コンポーネントになる必要がある.
    hooxは文法糖createContainerを提供し、文法を少し簡略化することができます.
    import { createContainer } from './store'
    import Counter from './counter'
    
    function App() {
      return 
    }
    
    export default createContainer(App)

    それでも少し煩わしい.特に、複数のstoreが互いに呼び出される場合、使用状態のコンポーネントが対応するProviderに包まれているかどうかに特に注意する必要があります.しかし、私は依然としてstamenhoxのように購読を発表する方法を使用したくない.reactにはすでに独自の応答論理があるからです.サブスクリプションをパブリッシュするロジックを追加します...私の能力は比較的に悪くて、holdは耐えられません...今のところ、もっと良い方法は考えられません.文法糖を提供して、少し簡略化するしかありません.

    いくつかのapiは初めて使うと区別がつかなくなります

    getHooxuseHooxsetHooxとか、確かにapiは見ていることが多くて、初めて使うと少しぼんやりします.また間違えて使うかもしれません.しかし、初心者用は注意してください.特別な要求はありません.コンポーネントにgetHooxを使用しないでください.この点をしっかり覚えておけば、基本的には走れば大丈夫です.
    しばらくの間、getHooxが非応答的にグローバル状態を取得していることがわかり、その後OKとなった.最近、チームにeslint-pluginの学生が再研究しています.その後、hooxのlintプラグインを書くのを手伝ってもらうと、一部の問題が改善されます.
    今のところ、私もこの問題を解決する他のもっと良い方法が見つかりません.私はこのいくつかのapiを持っていなければなりません.

    他の悪いところ


    コメントエリアに残す

    最後に書く


    このツールは、現在、私たちのチーム内で5~6人が使用しています.全体的に、口コミはまあまあですが、特に中小プロジェクトについてです.一部のコンポーネントは少し煩雑で、useMemoが山ほど来て、useCallbackが行く論理があります.hooxを通じて、これらの論理をhookから抽出すると、コードがすっきりします.また、これらの中小プロジェクトでは、dva/reduxなどのツールを導入するのは、確かに重いように見えます.関数コンポーネント+hooxにより、軽量レベルが保証され、グローバルステータス管理のシーンも満たされます.
    しかし、それは確かに多くの欠点があります.また、本当に99%のシーンを食べたいなら、セットツールを補充する必要があるかもしれません.lintプラグイン、さらにはredux-devtoolsのようなツールも含まれています.今の私は、まだ正式版を出す勇気がなくて、自分の部門を持って本を暗記する勇気もありません.だからこの文章は、金を掘ることを含めて、私は部門のコラムに送っていません.
    しかし、使いたいなら、基本的には安心して使えます.私たち自身はすでに複数のビジネスを使用しています.大きなバージョンではありません.breaking changeは不可能です.ただし、React状態管理ツールの最終状態とは限らない......将来、あなたたちはもっと良いものに出会うか、それとも移行を選ぶかもしれません.
    最後にまとめてみますと、問題は大きくありません.どうぞご利用ください.

    Github


    具体的なソースコードと詳細apiの紹介はgithubを参照してください.https://github.com/wuomzfx/hoox
    ソース部分については詳しく説明しないし、コードも何行もないので、見てみればわかります.