HooX:Reactの次世代ステータス管理ツールではないはず
9898 ワード
前言
実は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の声明に直接位置づけることはできません.
その後、私が本当に望んでいるのはどんなものなのかをまとめました.
だから目標は簡単で、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つあります.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下位層は
context
とuseState
に基づいて実現され、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に包まれているかどうかに特に注意する必要があります.しかし、私は依然として
stamen
とhox
のように購読を発表する方法を使用したくない.reactにはすでに独自の応答論理があるからです.サブスクリプションをパブリッシュするロジックを追加します...私の能力は比較的に悪くて、holdは耐えられません...今のところ、もっと良い方法は考えられません.文法糖を提供して、少し簡略化するしかありません.いくつかのapiは初めて使うと区別がつかなくなります
getHoox
、useHoox
、setHoox
とか、確かに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
ソース部分については詳しく説明しないし、コードも何行もないので、見てみればわかります.