Lors de la composition de composants, la tâche de personnaliser le contenu d'un composant se pose très souvent. Par exemple, nous avons un composant DatePicker et nous devons afficher différents boutons Appliquer dans différentes parties de l'application Web.
Pour résoudre de tels problèmes, chaque technologie populaire utilise aujourd'hui le concept de "machines à sous". Angular a ngContent , Vue , Svelte et WebComponents ont des slots. Et seule la bibliothèque React populaire n'a pas un concept à part entière des machines à sous aujourd'hui.
Il existe plusieurs approches pour résoudre ce problème dans React:
Un composant peut soit restituer entièrement tous ses enfants, soit "y accéder" via l' API React.Children et manipuler les enfants de manière ponctuelle
Un composant peut déclarer ce que l'on appelle les renderProps et rendre le contenu renvoyé par eux aux bons endroits:
<MyComponent renderFooter={data => (<h1>Bye, ${data.name}</h1>)}/>
L'approche renderProps est généralement bien connue et ne présente aucun défaut fondamental. À moins que ce ne soit pas très pratique de l'utiliser, en comparaison avec des slots à part entière. NPM a plusieurs bibliothèques, telles que react-view-slot , mais je ne pense pas qu'elles soient assez pratiques et, surtout, résolvent simplement le problème.
J'ai décidé d'essayer de corriger cette faille fatale , et maintenant je vais vous dire comment.
Je vois l'objectif - je ne vois pas l'implémentation
Avant de programmer quoi que ce soit, il est utile de savoir quelle API vous souhaitez obtenir. Voici à quoi ressemblait mon croquis du résultat souhaité:
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>
);
}
L'idée était de créer les emplacements nécessaires et de stocker le composant de fonction dans une propriété statique, puis de l'utiliser de manière appropriée sur les côtés d'envoi et de réception.
, . – , , , -, . , , 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/>.
Ainsi, en 20 lignes de code, nous avons implémenté un concept que beaucoup de développeurs dans d'autres technologies aiment vraiment, et qui manque à React.
J'ai publié l'implémentation terminée en tant que bibliothèque react-slotify sur GitHub et en tant que package sur NPM . Déjà dans TypeScript et avec prise en charge du paramétrage des slots . Je serais heureux de recevoir des critiques constructives.