عند تكوين المكونات ، غالبًا ما تنشأ مهمة تخصيص محتوى المكون. على سبيل المثال ، لدينا مكون DatePicker ونحتاج إلى عرض أزرار تطبيق مختلفة في أجزاء مختلفة من تطبيق الويب.
لحل مثل هذه المشاكل ، تستخدم كل تقنية شائعة اليوم مفهوم "الفتحات". يحتوي Angular على ngContent و Vue و Svelte و WebComponents بها فتحات. ومكتبة React الشهيرة هي الوحيدة التي ليس لديها مفهوم كامل للفتحات اليوم.
توجد عدة طرق لحل هذه المشكلة في React:
يمكن للمكون إما أن يجعل كل العناصر التابعة له بالكامل ، أو "التسلق" إليهم من خلال React.Children API والتلاعب بالأبناء
يمكن للمكون الإعلان عن ما يسمى "عرض الدعائم" ، وعرض المحتوى الذي يتم إرجاعه منها في الأماكن الصحيحة:
<MyComponent renderFooter={data => (<h1>Bye, ${data.name}</h1>)}/>
يُعد أسلوب "عرض الدعامة" معروفًا بشكل عام وليس به عيوب أساسية. ما لم يكن استخدامه غير مناسب جدًا ، مقارنةً بالفتحات الكاملة. يحتوي NPM على العديد من المكتبات ، مثل فتحة عرض التفاعل ، لكنني لا أعتقد أنها ملائمة بما فيه الكفاية ، والأهم من ذلك أنها تحل المشكلة ببساطة.
قررت أن أحاول إصلاح هذا الخلل القاتل ، والآن سأخبرك كيف.
أرى الهدف - لا أرى التنفيذ
قبل برمجة أي شيء ، من المفيد معرفة API الذي تريد الحصول عليه. هكذا بدا مخططي للنتيجة المرجوة مثل:
const Component = props => {
Component.NameSlot = useSlot(props.children);
return (
<div>
<h1>
<Component.NameSlot.Receiver>
Default value
</Component.NameSlot.Receiver>
</h1>
Hello {props.children}
</div>
);
};
function App() {
return (
<div>
Hello!
<Component>
Foo
<Component.NameSlot>
Bobobo
</Component.NameSlot>
</Component>
</div>
);
}كانت الفكرة هي إنشاء الفتحات اللازمة وتخزين مكون الوظيفة في خاصية ثابتة ، ثم استخدامها بشكل مناسب على جانبي الإرسال والاستقبال.
, . – , , , -, . , , API, :
import {createSlot} from 'react-slotify';
export const MySlot = createSlot();
export const Component = ({children}) => {
return (
<div>
This component contains slot:
<MySlot.Renderer childs={children}>
This is default slot content
</MySlot.Renderer>
<div>It also renders children: {children}</div>
</div>
);
};import {Component, MySlot} from './component';
const App = () => {
return (
<div>
<Component>
<MySlot>Slotted content</MySlot>
Other content
</Component>
</div>
);
};, API, , – MySlot, {children}, , MySlot.Renderer. , JS-, :
export function createSlot() {
const Slot = ({ children, showChildren }) => {
return showChildren ? children : null;
}
const Renderer = ({ childs, children }) => {
const slotted = React.Children.toArray(childs).find(child => {
return React.isValidElement(child) && child.type === Slot;
});
if (!slotted || !React.isValidElement(slotted)) {
return children;
}
return React.cloneElement(slotted, { showChildren: true });
};
Slot.Renderer = Renderer;
return Slot;
}-, 20 . , React- , . . – Slot. , :
export function createSlot() {
const Slot = ({ children, showChildren }) => {
return showChildren ? children : null;
}
return Slot;
}, Slot – , showChildren={true}. , , Slot .
– Renderer. – -, Slot-, , showChildren={true}:
const Renderer = ({ childs, children }) => {
const slotted = React.Children.toArray(childs).find(child => {
return React.isValidElement(child) && child.type === Slot;
});
if (!slotted || !React.isValidElement(slotted)) {
return children;
}
return React.cloneElement(slotted, { showChildren: true });
};, Renderer , , Slot . .
– Renderer Slot, : <MySlot.Renderer/>.
وهكذا ، في 20 سطرًا من التعليمات البرمجية ، قمنا بتطبيق مفهوم يحبه العديد من المطورين في التقنيات الأخرى حقًا ، والذي تفتقر إليه React.
لقد قمت بنشر التنفيذ النهائي كمكتبة تفاعلية على GitHub وكحزمة على NPM . بالفعل في TypeScript وبدعم معلمات الفتحات . سأكون سعيدا لتلقي النقد البناء.