من المقبول الآن التعيين إلى ref.current
، راجع المثال: https://reactjs.org/docs/hooks-faq.html#is -there-something-like -ثيل- variables
محاولة تعيين قيمة لها يعطي الخطأ: Cannot assign to 'current' because it is a constant or a read-only property.
@types/react
وواجهت مشاكل.Definitions by:
في index.d.ts
) حتى يتمكنوا من رد.العلاقات العامة موضع تقدير! يجب أن يكون تغيير سطر واحد سهل للغاية 😊
ferdaber ،
مرحبًا ، أحب اختيار هذه المشكلة. نظرًا لأنني جديد على الكتابة المطبوعة (أسبوع واحد فقط) وستكون هذه أول مساهمة لي في المصدر المفتوح.
لقد قمت بالاطلاع على node_modules/@types/react/index.d.ts وهناك الكود التالي في السطر رقم 61 الذي يعلن أن التيار مقروء فقط.
interface RefObject<T> {
readonly current: T | null;
}
إذا كانت هذه هي المشكلة ، فيمكنني حلها. هل يمكنك توجيه أول علاقات عامة خاصة بي؟
شكرا لك على وقتك :)
نعم ، إنها فقط تزيل المُعدِّل readonly
هذا السطر.
يُرجع React.useRef
MutableRefObject
لا تكون خاصية current
الخاصة به readonly
. أفترض أن السؤال هو ما إذا كان يجب توحيد أنواع كائن المرجع.
يجب أن تكون موحدة ، وقت تشغيل React ليس له قيود على خاصية current
التي تم إنشاؤها بواسطة React.createRef()
، الكائن نفسه مغلق ولكن غير مجمّد.
ليست كذلك. تركت عن قصد للقراءة فقط لضمان الاستخدام الصحيح ، حتى لو لم يتم تجميدها. المراجع التي تمت تهيئتها بالقيمة null دون الإشارة تحديدًا إلى أنك تريد أن تكون قادرًا على تخصيص قيمة خالية لها يتم تفسيرها على أنها مراجع تريد أن تُدار بواسطة React - أي أن React "تمتلك" التيار وأنت تشاهده فقط.
إذا كنت تريد كائن ref قابل للتغيير يبدأ بقيمة فارغة ، فتأكد أيضًا من إعطاء | null
للوسيطة العامة. هذا سيجعلها قابلة للتغيير ، لأنك "تملكها" ولا تتفاعل.
ربما يكون هذا أسهل بالنسبة لي لأن أفهمه لأنني عملت مع اللغات القائمة على المؤشر في كثير من الأحيان من قبل والملكية _ جدا_ مهمة فيها. وهذا هو المراجع ، مؤشر. يقوم .current
بإلغاء الإشارة إلى المؤشر.
هذا عادل ، لقد كنت أستخدم React.createRef()
كدالة مساعدة لإنشاء مؤشر فقط ، لأن React لن تديره إلا إذا تم تمريره كـ ref
prop ، ولكن يمكنك تمامًا مثل حسنًا ، قم بإنشاء كائن مؤشر بسيط بنفس الهيكل.
يمكننا تعديل createRef
ليكون لدينا نفس منطق التحميل الزائد ، ولكن نظرًا لعدم وجود وسيطة ، يجب أن يكون مع إرجاع من النوع الشرطي. إذا قمت بتضمين | null
فإنه سيعود MutableRefObject
، وإذا لم تقم بتضمينه فسيكون (غير قابل للتغيير) RefObject
.
هل سيعمل ذلك مع أولئك الذين ليس لديهم strictNullTypes
ممكّنًا؟
🤔
هل يجب أن نجعل من السهل كتابة رمز غير صحيح لأولئك الذين _do_ تم تمكين strictNullTypes
؟
عندما أنشأت هذه المشكلة ، لم أكن أدرك أنه كان هناك بالفعل هذا التحميل الزائد | null
. إنه ليس نمطًا شائعًا جدًا لذا لم أتوقع وجود شيء من هذا القبيل. لا أعرف كيف فاتني ذلك في الوثائق رغم أنه موثق وموضح جيدًا. لا بأس بإغلاق هذا باعتباره ليس مشكلة إلا إذا كنت تريد توحيد useRef
و createRef
.
نظرًا لأن المشكلة نفسها كانت تستند إلى useRef
وليس createRef
، فأنا موافق أيضًا على إغلاق هذا لأن قلة قليلة من الأشخاص هناك يعدلون مباشرة قيمة المؤشر التي تم إلغاء الإشارة إليها في createRef
.
أتساءل عما إذا كان نمط التحميل الزائد هذا سينتج بشكل جيد ، بالنظر إلى وثائق الخطافات ، نرى عددًا قليلاً من الاستخدامات useRef
مع قيمة افتراضية ، وفي هذه الحالة قد لا تكون قيمة .current
أبدًا null ولكن قد يكون من المزعج نوعًا ما للمستخدمين أن يستخدموا باستمرار عامل التأكيد غير الفارغ لتجاوزه.
هل سيكون من المنطقي أن تكون القيمة المعادة قابلة للتغيير إذا تم إعطاء قيمة أولية ، ولكنها غير قابلة للتغيير إذا تم حذفها؟ إذا تم تقديم قيمة أولية ، فمن المحتمل ألا يتم تمرير المرجع إلى أحد المكونات عبر ref
، ويستخدم أكثر كمتغير حالة. من ناحية أخرى ، عند إنشاء المرجع ليتم تمريره إلى أحد المكونات فقط ، فمن المحتمل ألا يتم توفير قيمة أولية.
هذا نوع مما كنت أفكر فيه. Kovensky ما هي أفكارك؟
ferdaber useRef
كما تم تعريفه الآن سيكون دائمًا قابلاً للتغيير _ و_ غير قابل للإلغاء ما لم تعطيه صراحةً وسيطة عامة لا تتضمن | null
وقيمة أولية null
.
يجب تهيئة المراجع التي تم تمريرها إلى مكون بـ null
لأن هذا هو ما تشير إليه React بتعيين المراجع عند إصدارها (على سبيل المثال عند إلغاء تثبيت مكون تم تثبيته بشروط). useRef
بدون تمرير قيمة سيجعلها تبدأ undefined
بدلاً من ذلك ، لذلك الآن عليك أيضًا إضافة | undefined
إلى شيء كان يجب أن يهتم فقط بـ | null
.
المشكلة في توثيق React واستخدام useRef
بدون قيمة أولية هي أن فريق React لا يبدو أنه يهتم كثيرًا بالفرق بين null
و undefined
. قد يكون هذا شيء تدفق.
على أي حال ، فإن الطريقة التي حددت بها useRef
تتناسب تمامًا مع حالة استخدام bschlenk ، فقط أن null
هي قيمة "الالتزام" ، للأسباب التي ذكرتها أعلاه.
آه ، يُظهر البحث عن كثب ذلك ، ومن المثير للاهتمام أنه تمت تهيئته كـ undefined
في مصدر React بدون معلمة. رائع لذا كل شيء خوخي ، يبدو مثل 👍
أعتقد أنه أمر جيد كما هو ، من الغريب أنه سواء قمت بتضمين | null
، فإن نوع current
لا يزال من الممكن أن يكون فارغًا ، فقط تغيرت قابلية التغيير. أيضًا ، دائمًا ما تمر مستندات React بشكل صريح فارغًا ، فهل من الصحيح حذف القيمة الأولية؟
إنه ليس كذلك بشكل عام ، وعلى الأقل في الماضي عندما أشرت إلى أنه في جزء من التوثيق قال فريق React أنه "حتى لو نجح" فليس المقصود أن يتم الالتزام به.
هذا هو السبب في أن الوسيطة الأولى لـ createContext
مطلوبة ، على سبيل المثال ، حتى إذا كنت تريد أن يبدأ السياق بـ undefined
. يجب عليك بالفعل تمرير undefined
.
يبدو أنه في بعض الاستخدامات لا يستخدم المعلمة:
https://reactjs.org/docs/hooks-faq.html#is -there-something-like -ثيل-variables
https://reactjs.org/docs/hooks-faq.html#how -to-get-the-previous-props-or-state
https://reactjs.org/docs/hooks-faq.html#how -to-read-an-غالبًا-change-value-from-usecallback
ferdaber هذا لأنهم لا يهتمون بالأنواع هناك. ما هو نوع current
في useRef()
(بدون وسيطات)؟ undefined
؟ any
؟ مهما كان الأمر ، حتى لو أعطيت وسيطة عامة ، يجب أن تكون |
ed مع undefined
. _And_ إذا كان مرجعًا تستخدمه كعنصر رد فعل مرجع (ليس فقط كمخزن محلي لمؤشر الترابط) ، فيجب عليك أيضًا | null
لأن React قد تكتب null
هناك.
الآن تجد نفسك current
هو T | null | undefined
بدلاً من T
فقط.
// you should always give an argument even if the docs sometimes miss them
// $ExpectError
const ref1 = useRef()
// this is a mutable ref but you can only assign `null` to it
const ref2 = useRef(null)
// this is also a mutable ref but you can only assign `undefined`
const ref3 = useRef(undefined)
// this is a mutable ref of number
const ref4 = useRef(0)
// this is a mutable ref of number | null
const ref5 = useRef<number | null>(null)
// this is a mutable ref with an object
const ref6 = useRef<React.CSSProperties>({})
// this is a mutable ref that can hold an HTMLElement
const ref7 = useRef<HTMLElement | null>(null)
// this is the only case where the ref is immutable
// you did not say in the generic argument you want to be able to write
// null into it, but you gave a null anyway.
// I am taking this as the sign that this ref is intended
// to be used as an element ref (i.e. owned by React, you're only sharing)
const ref8 = useRef<HTMLElement>(null)
// not allowed, because you didn't say you want to write undefined in it
// this is essentially what would happen if we allowed useRef with no arguments
// to make it worse, you can't use it as an element ref, because
// React might write a null into it anyway.
// $ExpectError
const ref9 = useRef<HTMLElement>(undefined)
نعم ، يعمل DX هذا بالنسبة لي ، والوثائق هي A ++ ، لذلك لا أعتقد أن معظم الناس سيشوش عليهم الأمر. شكرا لهذا التفصيل! بصراحة ، يجب فقط ربط هذه المشكلة في الكتابة :)
قد تكون هناك حالة لدعم useRef<T>()
وجعلها تتصرف مثل useRef<T | undefined>(undefined)
؛ ولكن مهما كان المرجع الذي تقوم به مع ذلك لا يزال لا يمكن استخدامه كعنصر مرجع ، تمامًا مثل التخزين المحلي للخيط.
المشكلة هي ... ماذا يحدث إذا لم تقدم حجة عامة ، وهو مسموح به؟ سوف يستنتج TypeScript {}
. النوع الافتراضي الصحيح هو unknown
لكن لا يمكننا استخدامه.
أتلقى هذا الخطأ مع الكود التالي:
~~~ شبيبة
// ...
السماح للفاصل الزمني = useRef
// ...
useEffect (() => {
فاصل ثابت = setInterval (() => {/ * افعل شيئًا * /} ، 1000) ؛
interalRef.current = فاصل زمني ؛ // في هذا السطر أتلقى الخطأ
return () => {
clearInterval(intervalRef.current);
}
})
// ...
~وعندما أقوم بإزالة readonly
هنا فإنه يعمل:~ شبيبة
واجهة RefObject
للقراءة فقط التيار: T | باطل؛
}
~~~
أنا جديد مع كل من خطافات البحث والنص المطبوع (فقط جربهما معًا) لذا قد يكون الكود الخاص بي خاطئًا
بشكل افتراضي ، إذا أنشأت مرجعًا بقيمة افتراضية null
وحددت معاملته العامة ، فأنت تشير إلى نيتك في امتلاك React المرجع. إذا كنت تريد أن تكون قادرًا على تغيير المرجع الذي تملكه ، فيجب أن تعلن ذلك على النحو التالي:
const intervalRef= useRef<NodeJS.Timeout | null>(null) // <-- the generic type is NodeJS.Timeout | null
ferdaber شكرا لذلك!
أخذ هذه المرحلة إلى أبعد من ذلك والنظر إلى نوع العائد current
، فهل يجب أن يكون T
بدلاً من T | null
؟ مع ظهور الخطافات ، ليس لدينا دائمًا حالة مفادها أن جميع المراجع قد تكون null
، لا سيما في الحالة المتكررة حيث يتم استدعاء useRef
مع مُهيئ غير فارغ.
متابعة من قائمة الأمثلة الممتازة في https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31065#issuecomment -446660394 ، إذا كتبت:
const numericRef = useRef<number>(42);
ما هو نوع numericRef.current
؟ ليس هناك _need_ لكي يكون number | null
.
إذا حددنا الأنواع والوظائف على النحو التالي:
interface RefObject<T> {
current: T;
}
function useRef<T>(initialValue: T): RefObject<T>;
function useRef<T>(initialValue: T|null): RefObject<T | null>;
function createRef<T>(): RefObject<T | null>;
من شأنها أن تنتج الاستخدامات والأنواع التالية:
const r1 = useRef(42);
// r1 is of type RefObject<number>
// r1.current is of type number (not number | null)
const r2 = useRef<number>(null);
// r2 is of type RefObject<number | null>
// r2.current is of type number | null
const r3 = useRef(null);
// r3 is of type RefObject<null>
// r3.current is of type null
const r4 = createRef<number>();
// r4 is of type RefObject<number | null>
// r4.current is of type number | null
هل هناك شيء خاطئ؟
بالنسبة للإجابة على السؤال "ما هو نوع useRef
غير المعامل؟" ، فإن الإجابة هي أن هذه المكالمة (على الرغم من الوثائق) غير صحيحة ، وفقًا لفريق React.
لقد أضفت الحمل الزائد مع | null _ على وجه التحديد _ كحمل زائد ملائم لمراجع DOM / المكوِّن ، لأنها تبدأ دائمًا فارغة ، ويتم إعادة تعيينها دائمًا إلى قيمة خالية عند إلغاء التحميل ، ولن تقوم أبدًا بإعادة تعيين التيار بنفسك ، فقط React.
للقراءة فقط هناك المزيد للحماية من الأخطاء المنطقية أكثر من تمثيل خاصية الكائن / getter-only المجمدة في JavaScript.
لا يمكنك إجراء ذلك إلا عن طريق الصدفة عندما يقدم كلاكما وسيطة عامة تقول إنك لا تقبل قيمة خالية أثناء تهيئة القيمة لتصبح خالية على أي حال. أي حالة أخرى ستكون قابلة للتغيير.
آه ، نعم ، أرى الآن أنه تمت إزالة حالة | null
MutableRefObject<T>
، مقارنةً بـ RefObject<T>
. لذا فإن حالة useRef<number>(42)
تعمل بالفعل بشكل صحيح. شكرا على التوضيح!
ماذا علينا أن نفعل بـ createRef
؟ في الوقت الحالي ، تقوم بإرجاع RefObject<T>
غير قابل للتغيير ، مما يتسبب في حدوث مشكلات في قاعدة الشفرة الخاصة بنا حيث نرغب في تمريرها واستخدامها بنفس طريقة (قابلة للتغيير) useRef
ref كائنات. هل هناك طريقة يمكننا من خلالها تعديل كتابة createRef
للسماح لها بتكوين كائنات ref قابلة للتغيير؟
(ويتم تعريف السمة ref
على أنها من النوع Ref<T>
والتي تتضمن RefObject<T>
، مما يجعل كل شيء غير قابل للتغيير. هذه مشكلة كبيرة بالنسبة لنا: حتى لو حصلنا على متغير المرجع من useRef
، لا يمكننا الاستفادة من حقيقة أنه غير قابل للتغيير من خلال مكالمة forwardRef
.)
حسنًا ... ربما يمكننا استخدام نفس الحيلة ، فقط لأنها لا تحتوي على وسيطات (وتبدأ دائمًا فارغة) ستحتاج إلى نوع شرطي.
: null extends T ? MutableRefObject<T> : RefObject<T>
يجب أن يفعل ويستخدم نفس المنطق. إذا قلت أنك تريد وضع القيم الخالية فيه ، فسيكون قابلاً للتغيير ، وإذا لم تقم بذلك فلا يزال غير قابل للتغيير بالمعنى الحالي.
هذه فكرة جميلة جدا. نظرًا لأن createRef
لا يأخذ أي معلمة ، فمن المفترض أنه يحتاج دائمًا إلى تضمين خيارات | null
(على عكس useRef
) ، لذلك ربما يحتاج ذلك إلى قول MutableRefObject<T | null>
؟
لم أتمكن من الحصول على أي منهما للعمل في TS. هنا ملعب TS تم تكوينه معه:
https://tinyurl.com/y75c32y3
يتم دائمًا اكتشاف النوع على أنه MutableRefObject
.
في رأيك ، ما الذي يمكننا فعله بـ forwardRef
؟ يعلن أن ref
هو Ref<T>
، والذي لا يتضمن إمكانية MutableRefObject
.
(حالة استخدامنا التي تسبب صعوبة هي حالة الدالة mergeRefs
التي تأخذ مصفوفة من المراجع ، والتي يمكن أن تكون إما مراجع وظيفية أو كائنات ref ، وتنشئ مرجعًا مجمعًا واحدًا [مرجع وظيفي] يمكن تمريره إلى أحد المكونات. يقوم هذا المرجع المجمع بتسليم أي عنصر مرجعي وارد إلى جميع المراجع المقدمة ، إما عن طريق الاتصال بهم إذا كانوا مراجع وظيفية ، أو عن طريق تعيين .current
إذا كانت كائنات مرجعية. ولكن وجود RefObject<T>
غير القابل للتغيير وعدم تضمين MutableRefObject<T> in Ref<T>
يجعل ذلك صعبًا. هل يجب أن أطرح مشكلة منفصلة للمرجع والمرجع
نحن لا نغير نوع useRef
للأسباب المفصلة أعلاه (على الرغم من أن createRef
لا يزال قيد المناقشة) ، هل لديك أي أسئلة حول الأساس المنطقي؟
هل يجب أن أطرح مشكلة منفصلة للعناصر المشمولة في https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31065#issuecomment -457650501؟
نعم ، دعنا نفصلها.
إذا كنت تريد كائن ref قابل للتغيير يبدأ بقيمة فارغة ، فتأكد أيضًا من إعطاء
| null
للوسيطة العامة. هذا سيجعلها قابلة للتغيير ، لأنك "تملكها" ولا تتفاعل.
شكرا على هذا! أدت إضافة قيمة خالية إلى useRef
حل المشكلة بالنسبة لي.
قبل
const ref = useRef<SomeType>(null)
// Later error: Cannot assign to 'current' because it is a constant or a read-only property.
بعد
const ref = useRef<SomeType | null>(null)
هل تم إنشاء تعليق آخر حول مكونات forwardRef؟ بشكل أساسي ، لا يمكنك إعادة توجيه المرجع وتغيير قيمته الحالية مباشرةً ، والتي أعتقد أنها جزء من نقطة إعادة توجيه المرجع.
لقد صنعت الخطاف التالي:
export const useCombinedRefs = <T>(...refs: Ref<T>[]) =>
useCallback(
(element: T) =>
refs.forEach(ref => {
if (!ref) {
return;
}
if (typeof ref === 'function') {
ref(element);
} else {
ref.current = element; // this line produces error
}
}),
refs,
);
ويظهر لي خطأ: "لا يمكن التعيين إلى" current "لأنها خاصية للقراءة فقط."
هل هناك طريقة لحلها دون تغيير current
إلى قابل للكتابة؟
لذلك ، سيتعين عليك الغش.
(ref.current as React.MutableRefObject<T> ).current = element;
نعم ، هذا غير سليم بعض الشيء ، لكن هذه هي الحالة الوحيدة التي يمكنني التفكير فيها حيث يكون هذا النوع من التخصيص متعمدًا وليس صدفة - أنت تكرر سلوكًا داخليًا لـ React وبالتالي يتعين عليك خرق القواعد.
يمكننا تعديل
createRef
ليكون لدينا نفس منطق التحميل الزائد ، ولكن نظرًا لعدم وجود وسيطة ، يجب أن يكون مع إرجاع من النوع الشرطي. إذا قمت بتضمين| null
فإنه سيعودMutableRefObject
، وإذا لم تقم بتضمينه فسيكون (غير قابل للتغيير)RefObject
.
شكر كثيرا
(ref.current as React.MutableRefObject<T>).current = element;
يجب ان يكون:
(ref as React.MutableRefObject<T>).current = element;
أنت تكرر سلوكًا داخليًا لـ React
هل هذا يعني أن المستندات هنا قديمة؟ لأنهم يصفون هذا بوضوح بأنه سير عمل مقصود ، وليس سلوكًا داخليًا.
تشير المستندات في هذه الحالة إلى المرجع الذي تملكه ، ولكن بالنسبة للمراجع التي تمررها كسمة ref
لعنصر HTML ، فيجب قراءتها لك فقط.
function Component() {
// same API, different type semantics
const countRef = useRef<number>(0); // not readonly
const divRef = useRef<HTMLElement>(null); // readonly
return <button ref={divRef} onClick={() => countRef.current++}>Click me</button>
}
سيئتي ، كان ينبغي التمرير لأعلى. حصلت على خطأ حول readonly
للمرجع الذي أمتلكه وافترض أن هذه هي الحالة نفسها. (أستخدم الآن سير عمل مختلفًا ولا يمكنني إعادة إنتاج الخطأ بعد الآن ، لسوء الحظ ...)
على كل حال ، شكرا جزيلا لك على الشرح!
ليس من الواضح ما إذا كان الجزء createRef
من الموضوع قد تم نقله - لكنني سأقوم بالنشر هنا أيضًا.
أستخدم مكتبة تنقل شهيرة لـ React Native (React Navigation). في وثائقه ، يستدعي عادةً createRef
ثم يغير المرجع. أنا متأكد من أن هذا يرجع إلى أن React لا تديرها (لا يوجد DOM).
هل يجب أن يكون نوع React Native مختلفًا؟
انظر: https://reactnavigation.org/docs/navigating-without-navigation-prop
تضمين التغريدة
لقد واجهت هذه المشكلة أيضًا عند بدء التنقل ، هل وجدت حلاً؟
تلخيص ما قرأته للتو: الطريقة الوحيدة لتعيين قيمة لمرجع تم إنشاؤه بواسطة createRef<T>
هو وضعها في كل مرة تستخدمها؟ ما هو السبب وراء ذلك؟ في هذه الحالة ، فإن readonly
يمنع عمليا الإعدادات من قيمة المرجع على الإطلاق ، وبالتالي يهزم الغرض الكامل من الوظيفة.
TLDR: إذا كانت القيمة الأولية الخاصة بك هي null
(التفاصيل: أو شيء آخر خارج معلمة النوع) ، فقم بإضافة | null
إلى معلمة النوع ، وهذا من شأنه أن يجعل .current
قادرًا ليتم تعيينها مثل العادي.
التعليق الأكثر فائدة
ليست كذلك. تركت عن قصد للقراءة فقط لضمان الاستخدام الصحيح ، حتى لو لم يتم تجميدها. المراجع التي تمت تهيئتها بالقيمة null دون الإشارة تحديدًا إلى أنك تريد أن تكون قادرًا على تخصيص قيمة خالية لها يتم تفسيرها على أنها مراجع تريد أن تُدار بواسطة React - أي أن React "تمتلك" التيار وأنت تشاهده فقط.
إذا كنت تريد كائن ref قابل للتغيير يبدأ بقيمة فارغة ، فتأكد أيضًا من إعطاء
| null
للوسيطة العامة. هذا سيجعلها قابلة للتغيير ، لأنك "تملكها" ولا تتفاعل.ربما يكون هذا أسهل بالنسبة لي لأن أفهمه لأنني عملت مع اللغات القائمة على المؤشر في كثير من الأحيان من قبل والملكية _ جدا_ مهمة فيها. وهذا هو المراجع ، مؤشر. يقوم
.current
بإلغاء الإشارة إلى المؤشر.