J'essaie de convertir cette <canvas>
animation cool que j'ai trouvée ici en un composant réutilisable React. Il semble que ce composant nécessite un composant parent pour le canevas et de nombreux composants enfants pour le function Ball()
.
Pour des raisons de performances, il serait probablement préférable de transformer le Balls
en composants sans état car il y en aura beaucoup. Je ne suis pas aussi familier avec la création de composants sans état et je me demandais où je devrais définir les fonctions this.update()
et this.draw
qui sont définies dans function Ball()
.
Les fonctions des composants sans état vont-elles à l'intérieur ou à l'extérieur du composant? En d'autres termes, laquelle des propositions suivantes est la meilleure?
1:
const Ball = (props) => {
const update = () => {
...
}
const draw = () => {
...
}
return (
...
);
}
2:
function update() {
...
}
function draw() {
...
}
const Ball = (props) => {
return (
...
);
}
Quels sont les avantages / inconvénients de chacun et l'un d'entre eux est-il meilleur pour des cas d'utilisation spécifiques tels que le mien?
Réponses:
La première chose à noter est que les composants fonctionnels sans état ne peuvent pas avoir de méthodes, il ne faut pas compter sur un appel
update
oudraw
sur un renduBall
s'il s'agit d'un composant fonctionnel sans état.Dans la plupart des cas, vous devez déclarer les fonctions en dehors de la fonction du composant afin de les déclarer une seule fois et de toujours réutiliser la même référence. Lorsque vous déclarez la fonction à l'intérieur, chaque fois que le composant est rendu, la fonction sera de nouveau définie.
Dans certains cas, vous devrez définir une fonction à l'intérieur du composant pour, par exemple, l'affecter en tant que gestionnaire d'événements qui se comporte différemment en fonction des propriétés du composant. Mais vous pouvez toujours définir la fonction à l'extérieur
Ball
et la lier aux propriétés, ce qui rend le code beaucoup plus propre et rend les fonctionsupdate
oudraw
réutilisables.// You can use update somewhere else const update (propX, a, b) => { ... }; const Ball = props => ( <Something onClick={update.bind(null, props.x)} /> );
Si vous utilisez des hooks , vous pouvez utiliser
useCallback
pour vous assurer que la fonction n'est redéfinie que lorsque l'une de ses dépendances (props.x
dans ce cas) change:const Ball = props => { const onClick = useCallback((a, b) => { // do something with a, b and props.x }, [props.x]); return ( <Something onClick={onClick} /> ); }
C'est la mauvaise façon :
const Ball = props => { function update(a, b) { // props.x is visible here } return ( <Something onClick={update} /> ); }
Lors de l'utilisation
useCallback
, la définition de laupdate
fonction dans leuseCallback
hook lui-même notre extérieur du composant devient une décision de conception plus que tout, vous devez prendre en compte si vous allez réutiliserupdate
et / ou si vous devez accéder à la portée de la fermeture du composant à, par exemple, lecture / écriture de l'état. Personnellement je choisis de le définir à l'intérieur du composant par défaut et de le rendre réutilisable uniquement si le besoin s'en fait sentir, pour éviter une sur-ingénierie dès le départ. En plus de cela, il est préférable de réutiliser la logique de l'application avec des hooks plus spécifiques, laissant les composants à des fins de présentation. La définition de la fonction en dehors du composant lors de l'utilisation de hooks dépend vraiment du degré de découplage de React que vous souhaitez pour la logique de votre application.la source
this.draw
fonction à l'intérieur du fichierBall
. Il utilise lectx
de ce qui serait le parent<canvas>
et utilise également lethis
mot - clé pour ce que serait leBall
composant enfant . Quelle serait la meilleure façon d'intégrer l'implémentation du composant sans état afin que ces deux propriétés soient accessibles?this
lors de l'utilisation de composants fonctionnels sans état, gardez cela à l'esprit. Pour le contexte du canevas, vous devriez le transmettre à tousBall
, cela ne sonne pas du tout.bind
spécifiquement dans Chrome avant 59 était encore plus lent que les fonctions fléchées. Et dans Firefox, cela fait aussi longtemps qu'ils fonctionnent tous les deux à la même vitesse. Donc, je dirais que dans de tels cas, il n'y a aucune différence quelle est la méthode préférée :)Something
composant de savoir qu'il y a un accessoire X dans son composant parent car il le rend conscient de son contexte. La même chose se produit pour la question que vous posez et l'exemple de code que j'ai écrit, cela dépend du contexte, qui est ignoré par souci de simplicité.Nous pouvons avoir des fonctions à l'intérieur de composants fonctionnels sans état, voici l'exemple:
const Action = () => { function handlePick(){ alert("test"); } return ( <div> <input type="button" onClick={handlePick} value="What you want to do ?" /> </div> ); }
Mais ce n'est pas une bonne pratique car la fonction
handlePick()
sera définie à chaque fois que le composant est appelé.la source
handlePick()
au-dessus / en dehors du composant commefunction handlePick() { ... }; const Action = () => { ... }
le résultat étant que handlePick n'est défini qu'une seule fois. Si vous avez besoin de données duAction
composant, vous devez les transmettre en tant que paramètres à lahandlePick
fonction.Nous pouvons utiliser le hook React
useCallback
comme ci-dessous dans un composant fonctionnel:const home = (props) => { const { small, img } = props const [currentInd, setCurrentInd] = useState(0); const imgArrayLength = img.length - 1; useEffect(() => { let id = setInterval(() => { if (currentInd < imgArrayLength) { setCurrentInd(currentInd => currentInd + 1) } else { setCurrentInd(0) } }, 5000); return () => clearInterval(id); }, [currentInd]); const onLeftClickHandler = useCallback( () => { if (currentInd === 0) { } else { setCurrentInd(currentInd => currentInd - 1) } }, [currentInd], ); const onRightClickHandler = useCallback( () => { if (currentInd < imgArrayLength) { setCurrentInd(currentInd => currentInd + 1) } else { } }, [currentInd], ); return ( <Wrapper img={img[currentInd]}> <LeftSliderArrow className={currentInd > 0 ? "red" : 'no-red'} onClick={onLeftClickHandler}> <img src={Icon_dir + "chevron_left_light.png"}></img> </LeftSliderArrow> <RightSliderArrow className={currentInd < imgArrayLength ? "red" : 'no-red'} onClick={onRightClickHandler}> <img src={Icon_dir + "chevron_right_light.png"}></img> </RightSliderArrow> </Wrapper>); } export default home;
Je reçois «img» de son parent et c'est un tableau.
la source
Si vous souhaitez utiliser des accessoires ou l'état du composant dans la fonction, cela doit être défini dans le composant avec useCallback.
function Component(props){ const onClick=useCallback(()=>{ // Do some things with props or state },[]) }
D'un autre côté, si vous ne voulez pas utiliser d'accessoires ou d'état dans la fonction, définissez cela en dehors du composant.
const computeSomethings=()=>{ // Do some things with params or side effects } function Component(props){ }
la source