Le tableau passé en tant qu'accessoire ne reflète pas les changements
P粉685757239
P粉685757239 2023-09-06 17:37:30
0
2
557

Cette question vise davantage à en savoir plus sur la façon dont React gère et réagit aux changements plutôt que sur la mise en œuvre, j'ai donc laissé immutable-props-apprach se développer un peu.

J'essaie d'obtenir le premier élément d'un tableau et de le supprimer du tableau d'origine qui est transmis au composant en tant qu'accessoire :

const ChildComponent = (
  arrayToChange: Array<any>[]
) => {
  const elementFromArray = arrayToChange.shift();
}

De la définition de la méthode shift() ::

La méthode

shift() supprime le premier élément du tableau et renvoie l'élément supprimé. Cette méthode modifie la longueur du tableau.

Bien que la variable elementFromArray contienne désormais les éléments du tableau, le tableau est complet, il n'est en aucun cas affecté et contient toujours tous les éléments.

Mais comment est-ce possible ? React doit transmettre les accessoires par référence, donc le tableau d'origine doit être affecté. Je comprendrais si React mettait en place des protections et que ces changements ne seraient pas reflétés chez le parent, cependant, j'aimerais quand même que les changements soient reflétés chez l'enfant. Je ne trouve rien d'utile pour expliquer ce comportement, la plupart des ressources ne mentionnent que des méthodes immuables pour les accessoires et comment trouver une solution de contournement, mais pas la raison ou la logique derrière cela.

Bien que la variable elementFromArray contienne désormais les éléments du tableau, le tableau est complet, il n'est en aucun cas affecté et contient toujours tous les éléments. Cependant, si j'utilise la méthode Push(), les modifications sont reflétées et arrayToChange contient un élément supplémentaire.

Ma question est la suivante : pourquoi arrayToChange réagit-il différemment à ces méthodes ? Si shift() ne modifie pas le contenu, j'espère que push() ne le fera pas non plus.

P粉685757239
P粉685757239

répondre à tous(2)
P粉182218860

function Parent() {
  const forceUpdate = useForceUpdate();
  const [letters] = React.useState(["a", "b", "c"]);

  return (
    <div className="bg-yellow">
      A: {JSON.stringify(letters)}
      <ChildShowArray array={letters} />
      B: {JSON.stringify(letters)}
      <ChildChangeArray arrayToChange={letters} />
      C: {JSON.stringify(letters)}
      <ChildShowArray array={letters} />
      D: {JSON.stringify(letters)}

      <hr />
      <button type="button" onClick={forceUpdate}>re-render</button>
    </div>
  );
}

function ChildChangeArray({ arrayToChange }) {
  const elementFromArray = arrayToChange.shift();
  
  return (
    <div className="bg-red">
      elementFromArray = {JSON.stringify(elementFromArray)}
    </div>
  );
}

function ChildShowArray({ array }) {
  return (
    <div className="bg-green">
      array = {JSON.stringify(array)}
    </div>
  );
}

// helper hook
function useForceUpdate() {
  const [_state, setState] = React.useState({});
  return React.useCallback(() => { setState({}) }, []);
}

ReactDOM.createRoot(document.querySelector("#root")).render(<Parent />)
.bg-yellow { background: yellow }
.bg-red { background: red }
.bg-green { background: green }
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>

Le comportement de l'extrait de code peut être expliqué si vous considérez le processus de rendu comme un algorithme de largeur d'abord.

JSX convertira :

<div className="bg-yellow">
  A: {JSON.stringify(letters)}
  <ChildShowArray array={letters} />
  B: {JSON.stringify(letters)}
  <ChildChangeArray arrayToChange={letters} />
  C: {JSON.stringify(letters)}
  <ChildShowArray array={letters} />
  D: {JSON.stringify(letters)}
</div>

Entrez le JavaScript suivant :

React.createElement("div", { className: "bg-yellow" },
  "A: ", JSON.stringify(letters),
  React.createElement(ChildShowArray, { array: letters }),
  "B: ", JSON.stringify(letters),
  React.createElement(ChildChangeArray, { arrayToChange: letters }),
  "C: ", JSON.stringify(letters),
  React.createElement(ChildShowArray, { array: letters }),
  "D: ", JSON.stringify(letters),
)

React.createElement(ChildShowArray, { array: letter }) 创建一个不会立即调用 ChildShowArray La structure du composant. Cela créera une sorte de structure/objet intermédiaire qui ne s'exécutera que lorsque le moteur de rendu le lui demandera.

Placez-le dans {...} (JSX 上下文)中的 JavaScript 直接作为参数传递,因此直接解析。这意味着 Parent 中的所有 {JSON.stringify(letters)} pour qu'il s'exécute avant l'exécution de tout code du composant enfant.

Une fois la construction de la structure parent terminée, le moteur de rendu accédera à chaque structure/objet intermédiaire et lui demandera d'effectuer le rendu. Cela se fait de haut en bas, c'est pourquoi le premier rendu ChildShowArray 渲染仍然显示完整数组的原因。然后渲染 ChildChangeArray 并删除第一个元素。第二个 ChildShowArray reflète ce changement et est rendu sans le premier élément.

Veuillez noter, shift() 确实会更改 letters 的内容,但是当调用它时,Parent 的内容已经呈现并且不再变化。此更改确实会影响下一次渲染的Parent (cliquez sur le bouton "Render" dans l'extrait). Cela affecte également le rendu des autres composants enfants situés en dessous et qui utilisent la même référence de tableau.

P粉287726308

Je ne suis pas entièrement sûr de la question, mais je vais tenter une supposition ici, alors s'il vous plaît laissez un commentaire ici et je le modifierai avant de voter.

Je pense que vous devriez essayer ceci dans votre composant enfant :

const [data, setData] = React.useState(arrayToChange);

React.useEffect(() => setData(arrayToChange), [arrayToChange.length]);

Ensuite, utilisez "data" pour mapper la sortie dans jsx

Puis dans le composant parent, déplacez arrayToChange. Vous pouvez considérer useEffect comme un "observateur" qui se déclenchera lorsque la longueur du tableau change.

Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal