لدي الكثير من السياقات ويجب أن أكتب بهذه الطريقة ، إنها قبيحة للغاية! يعيق عملي الآن. مثل هذا التصميم يجعلها غير قابلة للاستخدام تقريبًا.
<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>
تقدم Hooks API القادمة طريقة مختلفة لاستهلاك السياقات.
شكرا لك. لكن ماذا عن المزود؟
انا سوف اكون صادق. إذا كنت تواجه هذا النوع من التنفيذ ، فإن تصميمك الهندسي يبدو سيئًا وربما لا يجب عليك استخدام سياق React.
كلا ، أنا أصمم حاوية متجر جديدة. يحتاج إلى العمل مع السياق في رد الفعل.
https://github.com/rabbitooops/rako
ما علاقة هذه المكتبة بخمس طبقات عميقة من المزودين / المستهلكين؟
لأنه يدعم حقن مخازن متعددة للتفاعل بدلاً من حل متجر واحد مثل Redux.
في هذه الحالة ، يكون التعامل مع السياق الآن على مستخدمي المكتبة ، وبدرجة أقل على المكتبة. إن كيفية استخدامهم للميزات أمر متروك لهم ، وإذا كانوا يريدون جميع مقدمي الخدمة في مكان واحد (وهو ما يتعارض مع الغرض من وجود متاجر متعددة) ، فهذا اختيارهم. من الناحية المثالية ، سيتم تنفيذ حل متجر متعدد في تقسيمات مختلفة في التطبيق ، لذا فإن السياقات المتداخلة مثل هذه أكثر ندرة.
2 سنتي على الأقل.
لذا فإن واجهة برمجة تطبيقات السياق ملائمة فقط لحل المتجر الفردي مثل redux.
على الاطلاق. لكن من الصعب أيضًا مناقشة مشكلتك دون تقديم مثال أكثر واقعية. الرجاء إنشاء واحد؟
تخيل أن هناك ثلاثة متاجر موضوع ، مستخدم وعداد.
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؟
اريد ان اسال سؤال لماذا لا تدعم الاستجابة باستهلاك سياقات متعددة في الفصل؟ يبدو أن واجهة برمجة تطبيقات hooks بها الكثير من المشكلات التي يتعين حلها وهي غير مستقرة جدًا في الوقت الحالي
static contextType = [themeContext, userContext]
const [theme, user] = this.context
سأقوم بتنفيذ StoreProviders
والذي يمكنه تضمين سياقات متعددة تلقائيًا.
const StoreProviders = constructStoreProviders(...storeContexts)
<StoreProviders>
<Child />
</StoreProviders>
شكرا على مساعدتك دان! :)
rabbitooops ما هي بالضبط مشكلات الخطافات التي لديك؟ أستخدم الخطافات في الإنتاج وهي تعمل جيدًا لفريقي.
وماذا عن هذا؟ من الآمن استخدام الخطاف الآن. تضمين التغريدة
// 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
أو بطريقة غير كاملة في جافا سكريبت:
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>
يبدو أن واجهة برمجة تطبيقات hooks بها الكثير من المشكلات التي يتعين حلها وهي غير مستقرة جدًا في الوقت الحالي
نحن نستعد لإصداره في غضون أسبوع أو أسبوعين - لست متأكدًا من سبب استنتاجك لذلك. ستكون جاهزة قريبًا على الرغم من أنك إذا كنت تريد أن تكون آمنًا ، فيرجى الانتظار حتى إصدار مستقر.
وماذا عن هذا؟
لا يُسمح عمومًا باستدعاء الخطافات في حلقة (كما تفعل في forEach
). من السهل التسبب في حدوث مشكلات بهذه الطريقة.
useStoreProviders
كلا useProvider
و useShouldComponentUpdate
يمثلان مشكلة مثل الخطافات (وهذا هو سبب عدم وجودهما في React). انظر إجابتي في https://github.com/facebook/react/issues/14534#issuecomment -455411307.
بشكل عام ، أجد صعوبة في فهم الغرض من هذه المشكلة.
يتم حل استهلاك سياقات متعددة عن طريق ربط useContext
. لا نوصي "بأتمتة" المصفوفات بطريقة ما لأن هذا يجعل من السهل جدًا كتابة مكونات تشترك في الكثير من السياق وإعادة العرض كثيرًا. يجب أن يكون لديك فكرة واضحة عن السياقات التي يستمع إليها المكون ، وهو ما يمنحك useContext
API. إذا كان لا بد من ذلك ، يمكنك كتابة useMyContexts()
Hook الذي يستخدم سياقات محددة بشكل صريح. أنا فقط لا أوصي بجعلها ديناميكية كما فعلت لأنه إذا تغير طول الصفيف ، يمكن أن ينكسر.
يمكن اعتبار وضع العديد من مقدمي الخدمة على أنه "نموذجي" وقد يكون لدينا في النهاية حل لذلك. لكنني أيضًا لا أفهم لماذا تعتبرها مشكلة كبيرة. الأمثلة في هذا الموضوع ليست واقعية بما يكفي لشرح المشكلة لي. لا أرى أي شيء سيئًا في تداخل عدة طبقات من JSX في مكان ما في الجزء العلوي من التطبيق. جميلة بالتأكيد لديك أكثر عمقا div
تعشش في معظم مكوناتها والتي لا تؤذي كثيرا.
سأختتم هذا لأنني أجبت بالفعل على هذه النقاط ، والمناقشة تدور في دوائر. إذا كان هناك شيء مفقود أعلمني.
الوقت الإضافي : useRender
أو شيء ما لتحكم أكثر في العرض؟ على سبيل المثال:
useRender(() => <div />, [...props])
الوسيط الثاني له نفس دور الخطاف useEffect
.
useMemo
صديقك.
راجع المقتطف الثاني في https://reactjs.org/docs/hooks-faq.html#how -to-memoize-calculations.
انتهى بي الأمر برمز كهذا:
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>
}
تضمين التغريدة
لا أرى أي شيء سيئًا في تداخل عدة طبقات من 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://github.com/disjukr/join-react-context
هذا بالتأكيد شيء يحدث في التطبيقات طوال الوقت. 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
يوضح ما يلي كيف أمرر المستخدم المصادق إلى المكونات التي تحتاج إليه.
قررت إنشاء دولة واحدة لطلبي. في ملف 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;
أعتقد أن هذه الطريقة تمنحني الحرية في بناء الدولة التي أحتاجها دون إضافة الكثير من السياقات الإضافية وتساعدني على تجنب عش عميق من مقدمي الخدمات.
تقدم Hooks API القادمة طريقة مختلفة لاستهلاك السياقات.
كيف يمكنني استخدام هذا في مكون الفصل؟
تقدم Hooks API القادمة طريقة مختلفة لاستهلاك السياقات.
https://reactjs.org/docs/hooks-reference.html#usecontextكيف يمكنني استخدام هذا في مكون الفصل؟
ألا تُستخدم الخطافات للاستفادة من ميزات React المختلفة دون كتابة فئات؟
حسنًا ، هذا هو ، كل ما تفعله الخطافات المختلفة موجود بالفعل في الفصول الدراسية. إذا كنت تتحدث عن البنية الملائمة والاستخدام API ، فإن التفاعل ينتقل بعيدًا عن الفئات إلى المكونات الوظيفية ، لذا مرحبًا بك في الوظائف والخطافات)
لقد أنشأت حزمة لحل المشكلة من خلال توفير واجهة برمجة تطبيقات مماثلة مع vue3
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 ، وهناك أيضًا العديد من المقترحات على مستوى اللغة لهذه العملية التي تشبه خط الأنابيب. ليست هناك حاجة لحلها باستخدام lib آخر.
التعليق الأكثر فائدة
@ 0xorial ، فأنت لا تحتاج حقًا إلى مكون لذلك ، بعد كل شيء <>> هي مجرد وظيفة استدعاء React.createElement. لذلك يمكنك تبسيطها إلى وظيفة إنشاء مثل:
واستخدامها على النحو التالي: