2 votes

Mise à jour de l'état d'un composant rendu récursivement dans React.js

J'ai besoin de mettre à jour un objet profondément imbriqué dans un état React à partir d'un composant rendu récursivement. Les éléments ressemblent à ceci et peuvent être imbriqués dynamiquement :

const items = [
  {
    id: "1",
    name: "Item 1",
    isChecked: true,
    children: []
  },
  {
    id: "3",
    name: "Item 3",
    isChecked: false,
    children: [
      {
        id: "3.1",
        name: "Child 1",
        isChecked: false,
        children: [
          {
            id: "3.1.1",
            name: "Grandchild 1",
            isChecked: true,
            children: []
          },
          {
            id: "3.1.2",
            name: "Grandchild 2",
            isChecked: true,
            children: []
          }
        ]
      },
      {
        id: "3.2",
        name: "Child 2",
        isChecked: false,
        children: []
      }
    ]
  }
]

J'ai du mal à comprendre comment mettre à jour l'état du niveau supérieur à partir d'un composant profondément imbriqué, parce qu'il ne "connaît" que lui-même, et non l'ensemble de la structure de données.

class App extends React.Component {
  state = {
    items
  };

  recursivelyRenderItems(items) {
    return (
      <ul>
        {items.map(item => (
        <li key={item.id}>
          {item.name}
          <input type="checkbox" checked={item.isChecked} onChange={(event) => {
            // TODO: Update the item's checked status
          }} />
          {this.recursivelyRenderItems(item.children)}
        </li>
    ))}
      </ul>

    )
  }

  render() {
    return (
      <div>
        {this.recursivelyRenderItems(this.state.items)}
      </div>
    );
  }
}

Comment puis-je y parvenir, s'il vous plaît ?

0voto

mikeb Points 5088

Cela fonctionne dans le violon que vous avez posté.

Fondamentalement, chaque composant a besoin de connaître son item.isChecked et de son parent isChecked . Créez donc un composant qui prend 2 accessoires, item y parentChecked où le dernier est un booléen du parent et le premier devient une variable d'état mutable dans le constructeur.

Ensuite, le composant met simplement à jour son état vérifié dans le gestionnaire d'événements et tout se déroule dans la méthode de rendu :

import React from "react";
import { render } from "react-dom";
import items from "./items";

class LiComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      item: props.item
    }
  }

  render() {
    var t = this;
    return (
      <ul>
            <li key={t.state.item.id}>
              {t.state.item.name}
              <input
                type="checkbox"
                checked={t.state.item.isChecked || t.props.parentChecked}
                onChange={event => {
                  t.setState({item: {...t.state.item, isChecked: !t.state.item.isChecked}})
                }}
              />
            </li>
            {t.state.item.children.map(item => (
              <LiComponent item={item} parentChecked={t.state.item.isChecked}/>
            ))}
      </ul>
    );
  }
}
class App extends React.Component {
  state = {
    items
  };

  render() {
    return (
      <div>
        {this.state.items.map(item => (
        <LiComponent item={item} parentChecked={false} />
        ))}
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));

https://codesandbox.io/s/treelist-component-ywebq

0voto

Johnny Zabala Points 244

Que pensez-vous de ceci ?

Créer un updateState qui prend deux arguments : id y newState . Puisque votre ids vous indiquent déjà l'élément que vous mettez à jour : 3, 3.1, 3.1.1 vous pouvez, à partir de n'importe quel élément récursif, appeler votre fonction de mise à jour à l'aide de l'élément id de l'élément à modifier. Divisez le id par des points et lancez vos éléments de manière récursive pour trouver le bon.

Par exemple, à partir de l'élément rendu récursif 3.1.1 peut appeler updateState comme ceci : updateState('3.1.1', newState) .

import React from "react";
import { render } from "react-dom";
import items from "./items";

class App extends React.Component {
  state = {
    items
  };

  updateState = item => {
    const idArray = item.id.split(".");
    const { items } = this.state;

    const findItem = (index, children) => {
      const id = idArray.slice(0, index).join(".");
      const foundItem = children.find(item => item.id === id);
      if (index === idArray.length) {
        foundItem.isChecked = !item.isChecked;
        this.setState(items);
        return;
      } else {
        findItem(index + 1, foundItem.children);
      }
    };

    findItem(1, items);
  };

  recursivelyRenderItems(items) {
    return (
      <ul>
        {items.map(item => (
          <li key={item.id}>
            {item.name}
            <input
              type="checkbox"
              checked={item.isChecked}
              onChange={event => this.updateState(item)}
            />
            {this.recursivelyRenderItems(item.children)}
          </li>
        ))}
      </ul>
    );
  }

  render() {
    return <div>{this.recursivelyRenderItems(this.state.items)}</div>;
  }
}

render(<App />, document.getElementById("root"));

Exemple de travail.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X