J'ai du mal à trouver la meilleure façon de procéder. Je peux utiliser un appel récursif avec requestAnimationFrame
pour avoir une boucle de jeu:
export interface Props { name: string; points: number; onIncrement?: () => void; onDecrement?: () => void; } class Hello extends React.Component<Props, object> { constructor(props: Props) { super(props); } render() { const { name, points, onIncrement, onDecrement } = this.props; return ( <div className="hello"> <div className="greeting"> Hello {name + points} </div> <button onClick={onDecrement}>-</button> <button onClick={onIncrement}>+</button> </div> ); } componentDidMount() { this.tick(); } tick = () => { this.props.onIncrement(); requestAnimationFrame(this.tick) } }
Mais que faire si je veux sur chaque image:
Je pourrais simplement avoir une autre boucle dans chaque composant, mais je crois comprendre que c'est une mauvaise pratique d'avoir plusieurs boucles requestAnimationFrame
en cours et c'est un impact significatif sur les performances.
Je suis donc perdu ici. Comment puis-je faire en sorte qu'un autre composant utilise la même boucle? (Si c'est même la meilleure façon de procéder!)
3 Réponses :
Vous devez créer un composant parent qui exécute la boucle, puis le transmet aux autres composants, il devrait ressembler à ceci:
<Loop> <ComponentX loop={loop} /> <ComponentY loop={loop} /> <ComponentZ loop={loop} /> </Loop>
Une solution serait de définir un tableau tel que les callbacks
dans le cadre de votre état. Au début du cycle de vie de chaque composant, ajoutez une fonction à ce tableau qui fait ce que vous voulez pour chaque boucle. Ensuite, appelez chaque fonction de votre boucle rAF comme ceci:
update( performance.now()) // Update loop function update( timestamp ) { // Execute callback state.callbacks.forEach( cb => cb( ...args )) // Pass frame delta, etc. requestAnimationFrame( update ) }
Avec un peu de travail, vous pouvez ajuster cet exemple simple pour fournir un moyen de supprimer des fonctions de callbacks
pour autoriser la dynamique ajouter / soustraire des routines de la boucle de jeu par nom ou par signature.
Vous pouvez également passer un objet enveloppant la fonction qui contient également un entier que vous pouvez utiliser pour trier les rappels par priorité.
Vous avez besoin d'un composant parent qui appelle requestAnimationFrame
et itère sur un tableau de refs
des composants enfants qui doivent être mis à jour à chaque cycle, en appelant sa méthode update
(ou comme vous voulez l'appeler) :
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="app"></div>
body { margin: 0; padding: 16px; } .progressBarWrapper { position: relative; width: 100%; border: 3px solid black; height: 32px; box-sizing: border-box; margin-bottom: 16px; } .progressBarProgress { position: absolute; top: 0; left: 0; height: 100%; }
class ProgressBar extends React.Component { constructor(props) { super(props); this.state = { progress: 0, }; } update() { this.setState((state) => ({ progress: (state.progress + 0.5) % 100, })); } render() { const { color } = this.props; const { progress } = this.state; const style = { background: color, width: `${ progress }%`, }; return( <div className="progressBarWrapper"> <div className="progressBarProgress" style={ style }></div> </div> ); } } class Main extends React.Component { constructor(props) { super(props); const progress1 = this.progress1 = React.createRef(); const progress2 = this.progress2 = React.createRef(); const progress3 = this.progress3 = React.createRef(); this.componentsToUpdate = [progress1, progress2, progress3]; this.animationID = null; } componentDidMount() { this.animationID = window.requestAnimationFrame(() => this.update()); } componentWillUnmount() { window.cancelAnimationFrame(this.animationID); } update() { this.componentsToUpdate.map(component => component.current.update()); this.animationID = window.requestAnimationFrame(() => this.update()); } render() { return( <div> <ProgressBar ref={ this.progress1 } color="magenta" /> <ProgressBar ref={ this.progress2 } color="blue" /> <ProgressBar ref={ this.progress3 } color="yellow" /> </div> ); } } ReactDOM.render(<Main />, document.getElementById('app'));
Gardez à l'esprit, cependant, que si vous essayez de faire quelque chose de trop complexe et que vous voulez atteindre 60 ips
, React n'est peut-être pas le bon outil à utiliser. p >
De plus, setState
est asynchrone , donc lorsque vous l'appelez, vous ne faites que pousser une mise à jour dans une file d'attente que React traitera à un moment donné, ce qui pourrait en fait se produire dans l'image suivante ou plus tard.
J'ai profilé cet exemple simple à voir si c'était le cas et ce n'est pas le cas, en fait. Les mises à jour sont ajoutées à la file d'attente ( enqueueSetState
), mais le travail se fait tout de suite:
Cependant, je soupçonne que dans une application réelle où React a plus de mises à jour à gérer ou dans les futures versions de React avec des fonctionnalités telles que le time slicing, les mises à jour asynchrones avec des priorités ... le rendu peut en fait se produire dans un cadre différent.
Je suis d'accord, react ne convient pas au développement de jeux pour diverses raisons. La seule façon pour moi d'utiliser React dans le développement de jeux est d'envelopper mon jeu dans un seul composant qui ne rend qu'une seule annonce avec une seule requestAnimationFrame. React dans ce cas n'est utilisé que pour naviguer dans d'autres vues sans boucle de jeu.
@Danziger stackoverflow.com/questions/62653091/ … Pouvez-vous vérifier cette question?