私はたくさんの文脈を持っていて、このように書かなければならないので、醜いです! それは今私の仕事を妨げています。 そのようなデザインはそれをほとんど使用不可能にします。
<context1.Provider value={value1}>
<context2.Provider value={value2}>
<context3.Provider value={value3}>
<context4.Provider value={value4}>
<context5.Provider value={value5}>
</context5.Provider>
</context4.Provider>
</context3.Provider>
</context2.Provider>
</context1.Provider>
<context1.Consumer>
{value1 => <context2.Consumer>
{value2 => <context3.Consumer>
{value3 => <context4.Consumer>
{value4 => <context5.Consumer>
{value5 => (
null
)}
</context5.Consumer>}
</context4.Consumer>}
</context3.Consumer>}
</context2.Consumer>}
</context1.Consumer>
今後のフックAPIは、コンテキストを消費するための別の方法を提供します。
ありがとうございました。 しかし、プロバイダーはどうですか?
正直に言います。 この種の実装に遭遇した場合、アーキテクチャ設計は貧弱に見えるので、おそらくReactコンテキストを使用すべきではありません。
いいえ、新しいストアコンテナを設計しています。 それはreactでコンテキストと連携する必要があります。
https://github.com/rabbitooops/rako
そのライブラリは、プロバイダー/コンシューマーの5層の深さと何の関係がありますか?
Reduxのような単一ストアソリューションの代わりに、反応する複数のストアの注入をサポートしているためです。
その場合、コンテキスト処理はライブラリのユーザーに行われ、ライブラリには行われません。 機能をどのように利用するかは彼ら次第であり、すべてのプロバイダーを1か所に配置したい場合(複数のストアを持つという目的に反する場合)、それが彼らの選択です。 理想的には、マルチストアソリューションはアプリケーションのさまざまな分割で実装されるため、このようなネストされたコンテキストははるかにまれです。
少なくとも私の2セント。
したがって、コンテキストAPIは、reduxのような単一ストアソリューションにのみ適しています。
どういたしまして。 しかし、より現実的な例がなければ、問題について議論することも困難です。 作成してください?
ユーザーとカウンターの3つのストアテーマがあると想像してください。
function theme(getState) {
return {
color: 'white',
setColor(color) {
this.setState({color})
}
}
}
function user(getState) {
return {
name: '',
setName(name) {
this.setState({name})
}
}
}
function counter(getState) {
return {
value: 0,
increment() {
const {value} = getState()
this.setState({value: value + 1})
}
}
}
const [themeStore, userStore, counterStore] = createStores(theme, user, counter)
const [themeContext, userContext, counterContext] = createContexts(themeStore, userStore, counterStore)
class App extends React.Component {
render() {
return (
<themeContext.StoreProvider>
<userContext.StoreProvider>
<counterContext.StoreProvider>
<Child />
</counterContext.StoreProvider>
</userContext.StoreProvider>
</themeContext.StoreProvider>
)
}
}
class Child extends React.Component {
static contextType = [themeContext, userContext]
render() {
const [theme, user] = this.context
/* ... */
}
}
あなたの理想的な構文は何ですか?
context.writeを使用して、ネストせずに複数のコンテキストを消費することをサポートします。
ネストせずに複数のコンテキストを使用することは、すでにサポートされています。 (フック付き)
Context.writeにはRFCがあります。 非常に複雑な問題が発生するため、問題が解決するかどうかはわかりません。 しかし、RFCが開いている間は、この問題で何が実行可能かわかりません。 RFCの動機にすでにあるものを超えて追加するものはありますか?
質問したいのですが。 クラスで複数のコンテキストを消費することをサポートしないのはなぜですか? フックAPIには解決すべき問題がたくさんあり、現在非常に不安定なようです。😨🧐😥🤕
static contextType = [themeContext, userContext]
const [theme, user] = this.context
複数のコンテキストを自動的にネストできるStoreProviders
を実装します。
const StoreProviders = constructStoreProviders(...storeContexts)
<StoreProviders>
<Child />
</StoreProviders>
ダンの助けをありがとう! :)
@rabbitooopsフックに関して正確に問題があるのはどれですか? 私は本番環境でフックを使用していますが、フックは私のチームにとってうまく機能します。
そして、これはどうですか? 今はフックを使っても大丈夫です。 @gaearon
// A library
function useStoreProviders(Component, ...contexts) {
contexts.forEach(context => Component.useProvider(context, someValue))
}
// User code
function App(props) {
const [theme] = useState('white')
// Safe to use `component hook`.
App.useProvider(themeContext, theme)
App.useShouldComponentUpdate(() => {})
// Meanwhile, library can also use `component hook`.
useStoreProviders(App, storeContext1, storeContext2, storeContext3)
// Normal hook can't use `component hook`.
customHook()
/* ... */
}
<App />
decorateBeforeRender(App)
@rabbitooops https://github.com/reactjs/rfcs/issues/101
@rabbitooops単一のストアとシンボルをキーとして使用して、ストアのマルチレイヤーを模倣するのはどうですか?
data Store = Leaf Object | C Store Store
またはjavascriptで不完全な方法で:
const LEFT = Symbol('LEFT')
const RIGHT = Symbol('RIGHT')
function createLeafStore = return new Store({});
function createStore(leftChild :: Store, rightChild :: Store) {
return new Store({[LEFT]: leftChild, [Right]: rightChild})
}
@TrySoundの違い: App.useProvider
@zhujinxuan申し訳ありませんが、私はあなたを取得できません。
あなたが使用することができます@zhujinxuan暗黙の、例:
<Subscribe to={[AppContainer, CounterContainer, ...]}>
{(app, counter, ...) => (
<Child />
)}
</Subscribe>
フックAPIには解決すべき問題がたくさんあり、現在非常に不安定なようです
1、2週間以内にリリースの準備をしていますが、なぜそれを推測したのかわかりません。 安全を確保したい場合は、安定したリリースまでお待ちください。
そして、これはどうですか?
( forEach
)ループでフックを呼び出すことは、通常は許可されていません。 この方法で問題を引き起こすのは簡単です。
useStoreProviders
useProvider
とuseShouldComponentUpdate
はどちらもフックとして問題があります(これがReactにフックがない理由です)。 https://github.com/facebook/react/issues/14534#issuecomment-455411307で私の応答を参照して
全体として、私はこの問題の意図を理解するのに苦労しています。
複数のコンテキストの消費は、 useContext
フックによって解決されます。 配列を使用して「自動化」することはお勧めしuseContext
APIが提供するものです。 必要に応じて、特定のコンテキストを明示的に使用するuseMyContexts()
フックを作成できます。 配列の長さが変わると壊れてしまう可能性があるため、あなたのように動的にすることはお勧めしません。
複数のプロバイダーを配置することは「ボイラープレート」と見なすことができ、最終的にはこれに対する解決策が得られる可能性があります。 しかし、なぜそれが大きな問題だと思われるのかもわかりません。 このスレッドの例は、私に問題を説明するのに十分現実的ではありません。 アプリケーションの上部のどこかにJSXのいくつかのレイヤーをネストしても、何も悪いことはありません。 ほとんどのコンポーネントにdiv
ネストがはるかに深く、それほど害がないことを確認してください。
私はすでにこれらの点に答えたと思うので、これを閉じます、そして議論は輪になって行きます。 足りないものがあれば教えてください。
OT: @gaearon 、 useRender
ようなものを追加したり、レンダリングをより細かく制御したりする計画はありますか? 例えば:
useRender(() => <div />, [...props])
2番目の引数はuseEffect
フックと同じ役割を果たします。
useMemo
はあなたの友達です。
https://reactjs.org/docs/hooks-faq.html#how-to-memoize-calculationsの2番目のスニペットを参照して
私はそのようなコードになってしまいました:
function provider<T>(theProvider: React.Provider<T>, value: T) {
return {
provider: theProvider,
value
};
}
function MultiProvider(props: {providers: Array<{provider: any; value: any}>; children: React.ReactElement}) {
let previous = props.children;
for (let i = props.providers.length - 1; i >= 0; i--) {
previous = React.createElement(props.providers[i].provider, {value: props.providers[i].value}, previous);
}
return previous;
}
次に、私のトップレベルの提供コンポーネントで:
public render() {
return (
<MultiProvider
providers={[
provider(Context1.Provider, this.context1),
provider(Context2.Provider, this.context2),
provider(Context3.Provider, this.context3),
provider(Context4.Provider, this.context4),
provider(Context5.Provider, this.context5),
]}
><AppComponents />
</MultiProvider>
}
@gaearon
アプリケーションの上部のどこかにJSXのいくつかのレイヤーをネストしても、何も悪いことはありません。
その方法で注入できるようにしたい依存関係が約15あり、15レベルのインデントがあると私にはきれいに見えません:)
@ 0xorialは、そのためのコンポーネントを実際に持つ必要はありません。結局のところ、<>>はReact.createElementの関数呼び出しにすぎません。 したがって、次のような作成関数に簡略化できます。
const compose = (contexts, children) =>
contexts.reduce((acc, [Context, value]) => {
return <Context.Provider value={value}>{acc}</Context.Provider>;
}, children);
そしてそれを次のように使用します:
import Context1 from './context1';
import Context2 from './context2';
import Context3 from './context3';
...
import Context15 from './context15';
const MyComponent = (props) => {
// const value1..15 = ... get the values from somewhere ;
return compose(
[
[Context1, value1],
[Context2, value2],
[Context3, value3],
...
[Context15, value15],
],
<SomeSubComponent/>
);
}
私は過去にこのケースを処理するライブラリを作成しました: https :
これは間違いなく、アプリケーションで常に発生することです。 useContext
は、コンポーネント内のコンテキストデータを_消費_するのに最適ですが、複数のプロバイダーを備えたアプリでコンテキストを_提供_する必要がある場合はそれほど優れていません。
@alesmenzelsocialbakersソリューションのクロージャの代替案は
const composeProviders = (...Providers) => (Child) => (props) => (
Providers.reduce((acc, Provider) => (
<Provider>
{acc}
</Provider>
), <Child {...props} />)
)
const WrappedApp = composeProviders(
ProgressProvider,
IntentsProvider,
EntitiesProvider,
MessagesProvider
)(App)
ReactDOM.render(<WrappedApp />, document.getElementById('root'));
欠点は、特定のプロバイダーコンポーネントをそれぞれ作成する必要があることです。
例:
export const ProgressProvider = ({ children }) => {
const [progress, setProgress] = useState(0)
return (
<ProgressContext.Provider value={{ progress, setProgress }}>
{children}
</ProgressContext.Provider>
)
}
サービス構成に優れた状態管理ライブラリを作成しました。 これはプロバイダー地獄を回避するデモです。 気軽に試してみるか、ソース(100行のコード)を読んでください!
コンテキストプロバイダーを収集するための「スコープ」オブジェクトを導入し、次のようにします。
私もこれにひびを入れました。 これはうまくいくようです:
const composeWrappers = (
wrappers: React.FunctionComponent[]
): React.FunctionComponent => {
return wrappers.reduce((Acc, Current): React.FunctionComponent => {
return props => <Current><Acc {...props} /></Current>
});
}
使用法は次のとおりです。
const SuperProvider = composeWrappers([
props => <IntlProvider locale={locale} messages={messages} children={props.children} />,
props => <ApolloProvider client={client}>{props.children}</ApolloProvider>,
props => <FooContext.Provider value={foo}>{props.children}</FooContext.Provider>,
props => <BarContext.Provider value={bar}>{props.children}</BarContext.Provider>,
props => <BazContext.Provider value={baz}>{props.children}</BazContext.Provider>,
]);
return (
<SuperProvider>
<MainComponent />
</SuperProvider>
);
このヘルパーをnpmライブラリとして公開しましたreact-compose-wrappers
以下は、認証されたユーザーをそれを必要とするコンポーネントに渡す方法を示しています。
アプリケーション用に1つの状態を作成することにしました。 State.jsファイルで、初期状態、コンテキスト、レデューサー、プロバイダー、およびフックを設定しました。
import React, { createContext, useContext, useReducer } from 'react';
const INITIAL_STATE = {}
const Context = createContext();
const reducer = (state, action) =>
action
? ({ ...state, [action.type]: action[action.type] })
: state;
export const Provider = ({ children }) => (
<Context.Provider value={ useReducer(reducer, INITIAL_STATE) }>
{ children }
</Context.Provider>
);
const State = () => useContext(Context);
export default State;
次に、index.jsファイルで、アプリをプロバイダーにラップしました。
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from './State';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<Provider>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root'),
);
コンポーネントの状態を消費するには、フックを使用できます。 ディスパッチを使用して状態を更新することもできます。 たとえば、ユーザーを取得または設定したい場合です。
import React, {useEffect} from 'react';
import State from './State'
const ExampleComponent = () => {
const [{ user }, dispatch] = State();
useEffect(() => {
const getUser = async () => {
const data = await fetch('http://example.com/user.json'); // However you get your data
dispatch({ type: 'user', user: data });
}
getUser();
}, [dispatch]);
// Don't render anything until user is retrieved
// The user is undefined since I passed an empty object as my initial state
if(user === undefined) return null;
return(
<p>{user.name}</p>
);
}
export default ExampleComponent;
この方法により、大量のコンテキストを追加することなく、必要な状態を構築する自由が得られ、プロバイダーの深いネストを回避するのに役立つと思います。
今後のフックAPIは、コンテキストを消費するための別の方法を提供します。
これをクラスコンポーネントで使用するにはどうすればよいですか?
今後のフックAPIは、コンテキストを消費するための別の方法を提供します。
https://reactjs.org/docs/hooks-reference.html#usecontextこれをクラスコンポーネントで使用するにはどうすればよいですか?
フックは、クラスを作成せずにさまざまなReact機能を利用するために使用されていませんか?
つまり、さまざまなフックが行うことはすべて、クラスにすでに存在しています。 便利な構文と使用法のAPIについて話している場合、reactはクラスから機能コンポーネントに移行するため、関数とフックへようこそ)
vue3で同様のAPIを提供することにより、問題を解決するためのパッケージを作成しました
https://github.com/TotooriaHyperion/react-multi-provide
通知:
Outer.tsx
import React, { useMemo } from "react";
import { Providers, useCreateContexts, useProvide } from "../..";
import { createService, ServiceA } from "./service";
export const Outer: React.FC = ({ children }) => {
const contexts = useCreateContexts();
const service = useMemo(createService, []);
useProvide(contexts, ServiceA.id, service);
return <Providers contexts={contexts}>{children}</Providers>;
};
Inner2.tsx
import React from "react";
import { useContexts, useReplaySubject } from "../..";
import { ServiceA } from "./service";
export const Inner2: React.FC = () => {
const [
{
state$,
actions: { inc, dec },
},
] = useContexts([ServiceA.id]);
const count = useReplaySubject(state$);
return (
<>
<p>{count}</p>
<div>
<button onClick={inc}>Increment</button>
<button onClick={dec}>Decrement</button>
</div>
</>
);
};
これが私がそれをする方法です:
interface Composable {
(node: React.ReactNode): React.ReactElement
}
const composable1: Composable = (node)=>{
return <someContext.Provider>{node}</someContext.Provider>
}
function Comp({children}:{children?:React.ReactNode}){
return pipe(
composabl1, composabl2, composable3
)(children)
}
pipe
関数は、rxjsなどの多くの一般的なライブラリにあります。また、このパイプラインのような操作のための言語レベルの提案もいくつかあります。 別のライブラリを使用して「解決」する必要はありません。
最も参考になるコメント
@ 0xorialは、そのためのコンポーネントを実際に持つ必要はありません。結局のところ、<>>はReact.createElementの関数呼び出しにすぎません。 したがって、次のような作成関数に簡略化できます。
そしてそれを次のように使用します: