React: منع عمليات إعادة العرض باستخدام React.memo وخطاف useContext.

تم إنشاؤها على ١٩ مارس ٢٠١٩  ·  37تعليقات  ·  مصدر: facebook/react

هل تريد طلب ميزة أو الإبلاغ عن خطأ ؟

خلل برمجي

ما هو السلوك الحالي؟

لا يمكنني الاعتماد على البيانات من واجهة برمجة تطبيقات السياق باستخدام (الخطاف useContext) لمنع عمليات إعادة العرض غير الضرورية باستخدام React.memo

إذا كان السلوك الحالي عبارة عن خطأ ، فالرجاء تقديم خطوات إعادة الإنتاج ، وإذا أمكن عرضًا توضيحيًا بسيطًا للمشكلة. الصق الرابط إلى JSFiddle (https://jsfiddle.net/Luktwrdm/) أو مثال CodeSandbox (https://codesandbox.io/s/new) أدناه:

React.memo(() => {
const [globalState] = useContext(SomeContext);

render ...

}, (prevProps, nextProps) => {

// How to rely on context in here?
// I need to rerender component only if globalState contains nextProps.value

});

ما هو السلوك المتوقع؟

يجب أن يكون لدي وصول بطريقة ما إلى السياق في رد نداء الوسيطة الثانية React.memo لمنع التصيير
أو يجب أن يكون لدي إمكانية إرجاع مثيل قديم لمكوِّن التفاعل في جسم الوظيفة.

ما إصدارات React وأي متصفح / نظام تشغيل متأثر بهذه المشكلة؟
16.8.4

التعليق الأكثر فائدة

يعمل هذا كما تم تصميمها. هناك مناقشة أطول حول هذا الأمر في https://github.com/facebook/react/issues/14110 إذا كنت فضوليًا.

لنفترض لسبب ما أن لديك AppContext الذي تحتوي قيمته على خاصية theme ، وتريد فقط إعادة عرض بعض ExpensiveTree على appContextValue.theme التغييرات.

TLDR هو أنه في الوقت الحالي لديك ثلاثة خيارات:

الخيار 1 (مفضل): تقسيم السياقات التي لا تتغير معًا

إذا احتجنا فقط إلى appContextValue.theme في العديد من المكونات ولكن appContextValue نفسه يتغير كثيرًا ، يمكننا تقسيم ThemeContext من AppContext .

function Button() {
  let theme = useContext(ThemeContext);
  // The rest of your rendering logic
  return <ExpensiveTree className={theme} />;
}

الآن أي تغيير AppContext لن يؤدي إلى إعادة تقديم ThemeContext للمستهلكين.

هذا هو الإصلاح المفضل. إذن فأنت لست بحاجة إلى أي خطة إنقاذ خاصة.

الخيار 2: قسّم المكون الخاص بك إلى قسمين ، ضع memo بينهما

إذا لم تتمكن من تقسيم السياقات لسبب ما ، فلا يزال بإمكانك تحسين العرض عن طريق تقسيم مكون إلى قسمين ، وتمرير دعائم أكثر تحديدًا إلى السياق الداخلي. لا يزال بإمكانك تصيير الشكل الخارجي ، لكن يجب أن يكون رخيصًا لأنه لا يفعل أي شيء.

function Button() {
  let appContextValue = useContext(AppContext);
  let theme = appContextValue.theme; // Your "selector"
  return <ThemedButton theme={theme} />
}

const ThemedButton = memo(({ theme }) => {
  // The rest of your rendering logic
  return <ExpensiveTree className={theme} />;
});

الخيار 3: مكون واحد بداخله useMemo

أخيرًا ، يمكننا أن نجعل الكود الخاص بنا أكثر تفصيلاً ولكننا نحتفظ به في مكون واحد عن طريق تغليف قيمة الإرجاع بـ useMemo وتحديد تبعياتها. سيظل المكون الخاص بنا قيد التنفيذ ، لكن React لن تعيد تصيير الشجرة الفرعية إذا كانت جميع المدخلات useMemo متطابقة.

function Button() {
  let appContextValue = useContext(AppContext);
  let theme = appContextValue.theme; // Your "selector"

  return useMemo(() => {
    // The rest of your rendering logic
    return <ExpensiveTree className={theme} />;
  }, [theme])
}

قد يكون هناك المزيد من الحلول في المستقبل ولكن هذا ما لدينا الآن.

مع ذلك ، لاحظ أن الخيار 1 هو الأفضل - إذا تغير بعض السياق كثيرًا ، ففكر في تقسيمه .

ال 37 كومينتر

يعمل هذا كما تم تصميمها. هناك مناقشة أطول حول هذا الأمر في https://github.com/facebook/react/issues/14110 إذا كنت فضوليًا.

لنفترض لسبب ما أن لديك AppContext الذي تحتوي قيمته على خاصية theme ، وتريد فقط إعادة عرض بعض ExpensiveTree على appContextValue.theme التغييرات.

TLDR هو أنه في الوقت الحالي لديك ثلاثة خيارات:

الخيار 1 (مفضل): تقسيم السياقات التي لا تتغير معًا

إذا احتجنا فقط إلى appContextValue.theme في العديد من المكونات ولكن appContextValue نفسه يتغير كثيرًا ، يمكننا تقسيم ThemeContext من AppContext .

function Button() {
  let theme = useContext(ThemeContext);
  // The rest of your rendering logic
  return <ExpensiveTree className={theme} />;
}

الآن أي تغيير AppContext لن يؤدي إلى إعادة تقديم ThemeContext للمستهلكين.

هذا هو الإصلاح المفضل. إذن فأنت لست بحاجة إلى أي خطة إنقاذ خاصة.

الخيار 2: قسّم المكون الخاص بك إلى قسمين ، ضع memo بينهما

إذا لم تتمكن من تقسيم السياقات لسبب ما ، فلا يزال بإمكانك تحسين العرض عن طريق تقسيم مكون إلى قسمين ، وتمرير دعائم أكثر تحديدًا إلى السياق الداخلي. لا يزال بإمكانك تصيير الشكل الخارجي ، لكن يجب أن يكون رخيصًا لأنه لا يفعل أي شيء.

function Button() {
  let appContextValue = useContext(AppContext);
  let theme = appContextValue.theme; // Your "selector"
  return <ThemedButton theme={theme} />
}

const ThemedButton = memo(({ theme }) => {
  // The rest of your rendering logic
  return <ExpensiveTree className={theme} />;
});

الخيار 3: مكون واحد بداخله useMemo

أخيرًا ، يمكننا أن نجعل الكود الخاص بنا أكثر تفصيلاً ولكننا نحتفظ به في مكون واحد عن طريق تغليف قيمة الإرجاع بـ useMemo وتحديد تبعياتها. سيظل المكون الخاص بنا قيد التنفيذ ، لكن React لن تعيد تصيير الشجرة الفرعية إذا كانت جميع المدخلات useMemo متطابقة.

function Button() {
  let appContextValue = useContext(AppContext);
  let theme = appContextValue.theme; // Your "selector"

  return useMemo(() => {
    // The rest of your rendering logic
    return <ExpensiveTree className={theme} />;
  }, [theme])
}

قد يكون هناك المزيد من الحلول في المستقبل ولكن هذا ما لدينا الآن.

مع ذلك ، لاحظ أن الخيار 1 هو الأفضل - إذا تغير بعض السياق كثيرًا ، ففكر في تقسيمه .

كلا الخيارين سينقذان من تقديم الأطفال إذا لم يتغير الموضوع.

gaearon هل الأزرار هي الأطفال أم هل الأزرار تجعل الأطفال؟ أفتقد بعض سياق كيفية استخدام هذه.

سيؤدي استخدام الخيار 2 unstable_Profiler 2 إلى تشغيل عمليات الاسترجاعات onRender ولكن لن يتم استدعاء منطق العرض الفعلي. ربما أفعل شيئًا خاطئًا؟ ~ https://codesandbox.io/s/kxz4o2oyoo~ https://codesandbox.io/s/00yn9yqzjw

لقد قمت بتحديث المثال ليكون أكثر وضوحا.

سيؤدي استخدام الخيار 2 unstable_Profiler إلى تشغيل عمليات رد نداء onRender ولكن لن يتم استدعاء منطق العرض الفعلي. ربما أفعل شيئًا خاطئًا؟ https://codesandbox.io/s/kxz4o2oyoo

هذا هو بالضبط الهدف من هذا الخيار. :-)

ربما يكون الحل الجيد لذلك هو الحصول على إمكانية "أخذ" مكون السياق وإعادة العرض فقط في حالة إعطاء رد نداء إرجاع صحيح ، على سبيل المثال:
useContext(ThemeContext, (contextData => contextData.someArray.length !== 0 ));

المشكلة الرئيسية في الخطافات التي قابلتها بالفعل هي أننا لا نستطيع إدارة ما يتم إرجاعه بواسطة أحد المكونات من داخل الخطاف - لمنع العرض ، وإرجاع القيمة المحفوظة وما إلى ذلك.

إذا استطعنا ، فلن يكون قابلاً للتكوين.

https://overreacted.io/why-isnt-xa-hook/#not -a-hook-usebailout

الخيار 4: لا تستخدم السياق لنشر البيانات ولكن لاشتراك البيانات. استخدم useSubscription (لأنه من الصعب الكتابة لتغطية جميع الحالات).

هناك طريقة أخرى لتجنب إعادة التصيير.
"تحتاج إلى نقل JSX إلى مستوى أعلى من مكون إعادة العرض ، فلن تتم إعادة إنشائه في كل مرة"

مزيد من المعلومات هنا

ربما يكون الحل الجيد لذلك هو الحصول على إمكانية "أخذ" مكون السياق وإعادة العرض فقط في حالة إعطاء رد نداء إرجاع صحيح ، على سبيل المثال:
useContext(ThemeContext, (contextData => contextData.someArray.length !== 0 ));

المشكلة الرئيسية في الخطافات التي قابلتها بالفعل هي أننا لا نستطيع إدارة ما يتم إرجاعه بواسطة أحد المكونات من داخل الخطاف - لمنع العرض ، وإرجاع القيمة المحفوظة وما إلى ذلك.

بدلاً من صح / خطأ هنا ... هل يمكننا توفير وظيفة قائمة على الهوية سمحت لنا بتجميع البيانات من السياق؟

const contextDataINeed = useContext(ContextObj, (state) => state['keyICareAbout'])

حيث لا يظهر useContext في هذا المكون ما لم تكن نتيجة المحدد fn مختلفة من حيث الهوية عن النتيجة السابقة لنفس الوظيفة.

وجدت هذه المكتبة أنه قد يكون حل Facebook للتكامل مع الخطافات https://blog.axlight.com/posts/super-performant-global-state-with-react-context-and-hooks/

هناك طريقة أخرى لتجنب إعادة التصيير.
"تحتاج إلى نقل JSX إلى مستوى أعلى من مكون إعادة العرض ، فلن تتم إعادة إنشائه في كل مرة"

مزيد من المعلومات هنا

المشكلة هي أنه قد يكون من المكلف إعادة هيكلة شجرة المكونات فقط لمنع إعادة العرض من الأعلى إلى الأسفل.

fuleinist في النهاية ، لا يختلف الأمر كثيرًا عن MobX ، على الرغم من تبسيطه كثيرًا لحالة استخدام معينة. يعمل MobX بالفعل على هذا النحو (باستخدام البروكسيات أيضًا) ، حيث يتم تحور الحالة ويتم إعادة تقديم المكونات التي تستخدم أجزاء معينة من الحالة ، ولا شيء آخر.

gaearon لا أعرف ما إذا كنت

هنا عرض مع 1000 عنصر / مربع نص. ولكن في ذلك العرض التوضيحي ، لا تظهر أدوات التطوير إعادة التصيير. يجب عليك تنزيل المصادر على المستوى المحلي لاختبارها: https://codesandbox.io/embed/zen-firefly-d5bxk

import React, { createContext, useState, useContext, memo } from "react";

const FormContext = createContext();

const FormProvider = ({ initialValues, children }) => {
  const [values, setValues] = useState(initialValues);

  const value = {
    values,
    setValues
  };

  return <FormContext.Provider value={value}>{children}</FormContext.Provider>;
};

const TextField = memo(
  ({ name, value, setValues }) => {
    console.log(name);
    return (
      <input
        type="text"
        value={value}
        onChange={e => {
          e.persist();
          setValues(prev => ({
            ...prev,
            [name]: e.target.value
          }));
        }}
      />
    );
  },
  (prev, next) => prev.value === next.value
);

const Field = ({ name }) => {
  const { values, setValues } = useContext(FormContext);

  const value = values[name];

  return <TextField name={name} value={value} setValues={setValues} />;
};

const App = () => (
  <FormProvider initialValues={{ firstName: "Marr", lastName: "Keri" }}>
    First name: <Field name="firstName" />
    <br />
    Last name: <Field name="lastName" />
  </FormProvider>
);

export default App;

image

من ناحية أخرى ، يعمل هذا الأسلوب بدون سياق بشكل صحيح ، ولا يزال في التصحيح أبطأ مما كنت أتوقع ولكن إعادة العرض على الأقل لا بأس به

import React, { useState, memo } from "react";
import ReactDOM from "react-dom";

const arr = [...Array(1000).keys()];

const TextField = memo(
  ({ index, value, onChange }) => (
    <input
      type="text"
      value={value}
      onChange={e => {
        console.log(index);
        onChange(index, e.target.value);
      }}
    />
  ),
  (prev, next) => prev.value === next.value
);

const App = () => {
  const [state, setState] = useState(arr.map(x => ({ name: x })));

  const onChange = (index, value) =>
    setState(prev => {
      return prev.map((item, i) => {
        if (i === index) return { name: value };

        return item;
      });
    });

  return state.map((item, i) => (
    <div key={i}>
      <TextField index={i} value={item.name} onChange={onChange} />
    </div>
  ));
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

image

marrkeri لا أرى شيئًا خاطئًا في مقتطف الشفرة الأول. المكون الذي تم تمييزه في أدوات dev هو Field الذي يستخدم السياق ، وليس TextField وهو مكون مذكرة ويقوم بتنفيذ وظيفة areEqual.

أعتقد أن مشكلة الأداء في مثال Codeandbox تأتي من 1000 مكون تستخدم السياق. أعد تشكيله إلى مكون واحد يستخدم السياق ، قل Fields ، وقم بالعودة من هذا المكون (مع خريطة) TextField لكل قيمة.

marrkeri لا أرى شيئًا خاطئًا في مقتطف الشفرة الأول. المكون الذي تم تمييزه في أدوات dev هو Field الذي يستخدم السياق ، وليس TextField وهو مكون مذكرة ويقوم بتنفيذ وظيفة areEqual.

أعتقد أن مشكلة الأداء في مثال Codeandbox تأتي من 1000 مكون تستخدم السياق. أعد تشكيله إلى مكون واحد يستخدم السياق ، قل Fields ، وقم بالعودة من هذا المكون (مع خريطة) TextField لكل قيمة.

كما قلت كنت تحت نفس التفكير يجب تقديمه في كل مرة ولكن ( ) فقط عندما تتغير القيمة. ولكن من المحتمل أن تكون مجرد مشكلات في أدوات التطوير لأنني أضفت حشوة حولها وبدلا من ذلك يتم عرضه. تحقق من هذه الصورة
image

image

لم أفهم النقطة الثانية الخاصة بك حول إعادة البناء لمكون واحد . هل يمكن أن تفعل لقطة الثابتة والمتنقلة؟ وما رأيكم يا رفاق في الحد الأقصى للعدد المعروض من أيهما على ما يرام دون تأخير؟ هو 1000 الى كثير؟

marrkeri كنت أقترح شيئًا كهذا: https://codesandbox.io/s/little-night-p985y.

هل هذا هو سبب توقف رد الفعل-الإعادة

لذا يبدو أن هناك

الخيار 4: تمرير دالة اشتراك كقيمة سياق ، تمامًا كما هو الحال في عصر السياق القديم

قد يكون الخيار الوحيد إذا كنت لا تتحكم في الاستخدامات (مطلوب للخيارين 2-3) ولا يمكنك تعداد جميع المحددات الممكنة (مطلوبة للخيار 1) ، ولكنك لا تزال ترغب في كشف واجهة برمجة تطبيقات Hooks

const MyContext = createContext()
export const Provider = ({children}) => (
  <MyContext.provider value={{subscribe: listener => ..., getValue: () => ...}}>
    {children}
  </MyContext.provider>
)

export const useSelector = (selector, equalityFunction = (a, b) => a === b) => {
  const {subscribe, getValue} = useContext(MyContext)
  const [value, setValue] = useState(getValue())
  useEffect(() => subscribe(state => {
      const newValue = selector(state)
      if (!equalityFunction(newValue, value) {
        setValue(newValue)
      }
  }), [selector, equalityFunction])
}

Hypnosphi : توقفنا عن تمرير حالة المتجر في السياق (تنفيذ

لمزيد من التفاصيل ، راجع رسالتي تاريخ وتنفيذ React-Redux .

قرأت من خلال سلسلة الرسائل ولكن ما زلت أتساءل - هل الخيارات الوحيدة المتاحة اليوم لتقديمها بشكل مشروط عند تغيير السياق ، "الخيارات 1 2 3 4" المذكورة أعلاه؟ هل هناك شيء آخر قيد الإعداد لمعالجة هذا الأمر رسميًا أم أن "الحلول الأربعة" تعتبر مقبولة بدرجة كافية؟

لقد كتبت في الموضوع الآخر ، ولكن فقط في حالة. إليك حل غير رسمي.
مقترح useContextSelector ومكتبة use- Context -selector في أرض المستخدمين.

بصراحة ، هذا يجعلني أعتقد أن إطار العمل ليس جاهزًا للدخول بشكل كامل في الوظائف والخطافات مثلما يتم تشجيعنا. باستخدام طرق الفئات ودورة الحياة ، كان لديك طريقة منظمة للتحكم في هذه الأشياء ، والآن باستخدام الخطافات ، أصبح الأمر أسوأ بكثير من حيث عدم قابليته للقراءة

يبدو أن الخيار 3 لا يعمل. أفعل أي شيء خطأ؟ https://stackblitz.com/edit/react-w8gr8z

@ مارتن ، أنا لا أتفق مع ذلك.

يمكن قراءة نمط الخطاف من خلال التوثيق والتعليمات المنظمة جيدًا
يمكن استبدال كل من أصناف الهيكل و React ودورة الحياة بوظيفة وظيفية
منها مكافئة.

لسوء الحظ ، لا يمكن تحقيق النمط التفاعلي بواسطة React عبر
إما فئات React أو الخطافات.

يوم السبت 11 يناير 2020 الساعة 9:44 صباحًا Martin Genev [email protected]
كتب:

بصراحة ، هذا يجعلني أعتقد أن إطار العمل ليس جاهزًا للعمل
بشكل كامل في الوظائف والخطافات مثلما يتم تشجيعنا. مع الفصول
وطرق دورة الحياة لديك طريقة مرتبة للتحكم في هذه الأشياء و
الآن مع الخطافات هو أسوأ بكثير بناء الجملة أقل قابلية للقراءة

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/facebook/react/issues/15156؟email_source=notifications&email_token=AAI4DWUJ7WUXMHAR6F2KVXTQ5D25TA5CNFSM4G7UEEO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW1
أو إلغاء الاشتراك
https://github.com/notifications/unsubscribe-auth/AAI4DWUCO7ORHV5OSDCE35TQ5D25TANCNFSM4G7UEEOQ
.

mgenev الخيار 3 يمنع إعادة <ExpensiveTree /> ، الاسم يتحدث عن نفسه)

Hypnosphi شكرا ، كان هذا صحيحا.
https://stackblitz.com/edit/react-ycfyye

لقد أعدت كتابته والآن لا يتم إعادة تصيير مكونات العرض (العرض) الفعلية ، ولكن يتم عرض جميع الحاويات (السياق المتصل) عند أي تغيير في الدعامة في السياق ، بغض النظر عما إذا كانت قيد الاستخدام فيها. الآن الخيار الوحيد الذي يمكنني رؤيته هو البدء في تقسيم السياقات ، لكن بعض الأشياء عالمية حقًا وتلتف على أعلى مستوى ، وسيؤدي التغيير في أي دعامة فيها إلى إطلاق جميع الحاويات في التطبيق بالكامل ، لا أفعل لن أفهم كيف يمكن أن يكون ذلك جيدًا للأداء ...

هل يوجد في أي مكان مثال جيد على كيفية القيام بذلك بطريقة أداء؟ المستندات الرسمية محدودة حقًا

يمكنك تجربة خياري 4 ، وهو أساسًا ما يفعله رد الفعل والإعادة داخليًا
https://github.com/facebook/react/issues/15156#issuecomment -546703046

لتنفيذ وظيفة subscribe ، يمكنك استخدام شيء مثل Observables أو EventEmitter أو كتابة منطق الاشتراك الأساسي بنفسك:

function StateProvider({children}) {
  const [state, dispatch] = useReducer(reducer, initialState)
  const listeners = useRef([])
  const subscribe = listener => {
    listeners.current.push(listener)
  }
  useEffect(() => {
    listeners.current.forEach(listener => listener(state)
  }, [state])

  return (
    <DispatchContext.Provider value={dispatch}>
      <SubscribeContext.Provider value={{subscribe, getValue: () => state}}>
          {children}      
      </SubscribeContext.Provider>
    </DispatchContext.Provider>
  );
}

بالنسبة لأولئك الذين قد يكون لديهم اهتمامات ، إليك مقارنة بين مكتبات مختلفة مرتبطة إلى حد ما بهذا الموضوع.
https://github.com/dai-shi/will-this-react-global-state-work-in-concurrent-mode

آخر جهد لي هو التحقق من دعم تفريع الحالة بـ useTransition والذي سيكون مهمًا في الوضع المتزامن القادم.
في الأساس ، إذا استخدمنا حالة وسياق React بطريقة طبيعية ، فلا بأس بذلك. (وإلا فإننا بحاجة إلى خدعة ، على سبيل المثال ، هذا ).

شكرًا @ dai-shi ، أنا حقًا أحب حزمك وأنا أتطلع إلى اعتمادها.

@ dai-shi hi ، لقد وجدت للتو react-tracked lib ويبدو جيدًا حقًا إذا كان يحل مشكلات الأداء مع السياقات كما يعد. هل ما زال من الأفضل استخدام شيء آخر؟ أرى هنا مثالًا جيدًا على استخدامه ، كما أنه يعرض كيفية جعل مستوى البرمجيات الوسيطة باستخدام use-reducer-async https://github.com/dai-shi/react-tracked/blob/master/examples/12_async/src/store .ts شكرا لذلك. لقد قمت حاليًا بعمل شيء مشابه باستخدام useReducer ، مع التفاف dispatch مع واحد خاص بي للبرامج الوسيطة غير المتزامنة واستخدام Context ولكن القلق بشأن مشكلات أداء العرض في المستقبل بسبب التفاف السياقات.

bobrosoft react-tracked مستقر جدًا ، كما أقول (كمنتج مطور واحد بالتأكيد). التعليقات موضع ترحيب كبير وهذه هي الطريقة التي يمكن بها تحسين المكتبة. حاليًا ، يستخدم داخليًا ميزة غير موثقة لـ React ، وآمل أن نتمكن من استبدالها بميزة بدائية أفضل في المستقبل. يشبه use-reducer-async تقريبًا تركيبًا بسيطًا للسكر والذي لن يخطئ أبدًا.

عملت هذه HoC بالنسبة لي:
""
استيراد React ، {useMemo ، ReactElement ، FC} من "رد فعل" ؛
تخفيض الاستيراد من "لوداش / تقليل" ؛

اكتب المحدد = (السياق: أي) => أي ؛

محدد الواجهة {
}

const withContext = (
المكون: FC ،
السياق: أي ،
المحددات: SelectorObject ،
): FC => {
return (props: any): ReactElement => {
const Consumer = ({Context}: any): ReactElement => {
سياق CONProps = تقليل (
محددات
(acc: أي ، محدد: محدد ، مفتاح: سلسلة): أي => {
قيمة const = محدد (سياق) ؛
acc [مفتاح] = القيمة ؛
عودة لجنة التنسيق الإدارية
} ،
{} ،
) ؛
عودة useMemo (
(): ReactElement => و
[... Object.values ​​(props)، ... Object.values ​​(ContextProps)] ،
) ؛
} ؛
إرجاع (

{(السياق: أي): ReactElement => }

) ؛
} ؛
} ؛

تصدير الافتراضي withContext ؛
""

مثال على الاستخدام:

export default withContext(Component, Context, { value: (context): any => context.inputs.foo.value, status: (context): any => context.inputs.foo.status, });

يمكن اعتبار هذا بمثابة سياق مكافئ لـ redux mapStateToProps

لقد صنعت نظيرًا مشابهًا جدًا تقريبًا للاتصال () في إعادة الإرسال

const withContext = (
  context = createContext(),
  mapState,
  mapDispatchers
) => WrapperComponent => {
  function EnhancedComponent(props) {
    const targetContext = useContext(context);
    const { ...statePointers } = mapState(targetContext);
    const { ...dispatchPointers } = mapDispatchers(targetContext);
    return useMemo(
      () => (
        <WrapperComponent {...props} {...statePointers} {...dispatchPointers} />
      ),
      [
        ...Object.values(statePointers),
        ...Object.values(props),
        ...Object.values(dispatchPointers)
      ]
    );
  }
  return EnhancedComponent;
};

التنفيذ :

const mapActions = state => {
  return {};
};

const mapState = state => {
  return {
    theme: (state && state.theme) || ""
  };
};
export default connectContext(ThemeContext, mapState, mapActions)(Button);


تحديث: في النهاية ، قمت بالتبديل إلى EventEmitter للحصول على بيانات دقيقة ومتغيرة بسرعة مع مستمعين ديناميكيين (عند تحريك الماوس). أدركت أنني كنت أفضل أداة للوظيفة. يعتبر السياق أمرًا رائعًا لمشاركة البيانات بشكل عام ، ولكن ليس بمعدلات التحديث العالية.

ألا يتعلق الأمر بالاشتراك التصريحي أو عدم الاشتراك في السياق؟ التفاف مكون بشكل مشروط بمكون آخر يستخدم useContext ().

الشرط الرئيسي هو أن المكون الداخلي لا يمكن أن يكون له حالة ، لأنه سيكون فعليًا مثيلًا مختلفًا بسبب التفرع. أو ربما يمكنك تصيير المكون مسبقًا ثم استخدام cloneElement لتحديث الخاصيات.

بعض المكونات لا علاقة لها بالسياق في تصييرها ولكنها تحتاج إلى "قراءة قيمة فقط" منه. ماذا ينقصني؟
context

هل Context._currentValue آمن للاستخدام في الإنتاج؟

أحاول اشتراك مكونات في سياقات تكون رخيصة لتقديمها قدر الإمكان. ولكن بعد ذلك وجدت نفسي أقوم بتمرير الدعائم مثل الطريقة القديمة باستخدام رمز sphagetti أو استخدام المذكرة لتجنب الاشتراك للحصول على التحديثات عندما لا يكون هناك شيء منطقي مع التحديثات. تعد حلول المذكرات مفيدة عندما يعتمد عرض المكون الخاص بك على السياقات ولكن بخلاف ذلك ...

@ vamshi9666 هل تستخدم هذا كثيرا؟ هل تمكنت من ملاحظة إيجابيات / سلبيات نهجك؟ أنا أحب ذلك كثيرا. يعجبني التشابه مع الإعادة ولكن أيضًا كيف يحررك لكتابة إدارة حالة التطبيق والمنطق بحرية كسياق

لقد وجدت https://recoiljs.org/ حلاً جيدًا لهذه المشكلة. أعتقد أنه سيكون رائعًا إذا قمت بدمجه في React.

@ vamshi9666 هل تستخدم هذا كثيرا؟ هل تمكنت من ملاحظة إيجابيات / سلبيات نهجك؟ أنا أحب ذلك كثيرا. يعجبني التشابه مع الإعادة ولكن أيضًا كيف يحررك لكتابة إدارة حالة التطبيق والمنطق بحرية كسياق

لقد استخدمته في مكان واحد فقط وهدفي الأولي هو عزل إدارة الحالة عن jsx ولكن أيضًا ليس إنشاء نموذج غير معياري. لذلك عندما قمت بتوسيع وظيفة الطفرات ، بدا الأمر مشابهًا جدًا لمخفض إعادة التشغيل ونمط منشئ الإجراء. أيهما أفضل. لكني لا أرى فائدة لإعادة اختراع شيء ما ، في حين أنه موجود بالفعل.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات