J'ai un fichier JSON
avec plusieurs catégories, chaque catégorie a un nom avec un ensemble de champs de saisie avec leur propre nom et valeur.
Comment puis-je utiliser setState
pour mettre à jour les champs de valeur de chaque onChange
? Les catégories et les champs sont rendus en utilisant map ()
.
Je peux le faire fonctionner sans les champs imbriqués mais pas avec. Merci de toute assistance.
import React from "react"; const Item = ({ list, handleChange }) => { return ( <div className="item"> <label className="label">{list.name}</label> <input name={list.name} id={list.name} className="input" type="text" onChange={handleChange} value={list.amount} /> </div> ); }; export default Item;
import React from "react"; import Item from "./Item"; const Category = ({ name, list, handleChange }) => { return ( <div className="section"> <h3>{name}</h3> {list.fields.map(item => ( <Item id={item.name} name={item.name} key={item.name} list={item} handleChange={handleChange} /> ))} </div> ); }; export default Category;
import React, { Component } from "react"; import Category from "./Category"; import sampleData from "./sampleData"; class Main extends Component { constructor(props) { super(props); this.state = { list: sampleData }; } handleChange = e => { this.setState({ ??? }); }; render() { return ( <div> {this.state.list.map(item => ( <Category id={item.catName} name={item.catName} key={item.catName} list={item} handleChange={this.handleChange} /> ))} </div> ); } } export default Main;
[{ "catName": "Category 1", "fields": [ { "name": "field 1", "amount": "0" }, { "name": "field 2", "amount": "0" } ] }, { "catName": "Category 2", "fields": [ { "name": "field 1", "amount": "0" }, { "name": "field 2", "amount": "0" } }]
4 Réponses :
Votre JSON n'est pas valide. Vous avez également oublié de vérifier si list
contient déjà des données.
Essayez ceci:
Dans votre méthode handleChange
, assurez-vous d'utiliser le balisage JSON correct . Vous avez oublié la fermeture }
:
constructor(props) { super(props); this.state = { list: [] }; }
Dans la méthode de rendu de votre classe Main
, vérifiez si la liste est un tableau et si sa longueur est supérieure à 0. Cela évitera toute erreur de rendu, au cas où une valeur de type non tableau serait définie.
{Array.isArray(this.state.list) && this.state.list.length < 0 && this.state.list.map(item => ( <Category id={item.catName} name={item.catName} key={item.catName} list={item} handleChange={this.handleChange} /> ))}
Assurez-vous également de définir un tableau vide dans le constructeur de votre classe principale:
this.setState({ list: [{ "catName": "Category 1", "fields": [ { "name": "field 1", "amount": "0" }, { "name": "field 2", "amount": "0" } ] }, { "catName": "Category 2", "fields": [ { "name": "field 1", "amount": "0" }, { "name": "field 2", "amount": "0" } ]} ]})
Commençons de bas en haut
vous devez fournir à Item.js l'identifiant de sa catégorie parente par
le changement est id en id = {$ {name}, $ {item.name}}
. sera également agréable d'ajouter l'événement onClick pour nettoyer les données précédentes
le composant de catégorie à fournir est l'identifiant du composant d'élément
voici le résultat:
Main.js
import React from "react"; import Item from "./Item"; const Category = ({ name, list, handleChange }) => { return ( <div className="section"> <h3>{name}</h3> {list.fields.map(item => ( <Item id={`${name},${item.name}`} name={item.name} key={item.name} list={item} handleChange={handleChange} /> ))} </div> ); }; export default Category;
Item.js p >
import React from "react"; const Item = ({ list, handleChange ,id}) => { return ( <div className="item"> <label className="label">{list.name}</label> <input name={list.name} id={id} className="input" type="text" onChange={handleChange} onClick={e=>e.target.value=""} value={list.amount} /> </div> ); }; export default Item;
Category.js
import React, { Component } from "react"; import Category from "./Category"; import sampleData from "./sampleData"; class Main extends Component { constructor(props) { super(props); this.state = { list: sampleData }; } createNewData = (mainAccess, property, value) => { let newData = sampleData; newData.forEach(category => { if (category["catName"] === mainAccess) { debugger; category["fields"].forEach(item => { if (item["name"] === property) { console.log(item["amount"]); item["amount"] = value; } }); } }); return newData }; handleChange = e => { const propertyAccess = e.target.id.split(","); const newData = this.createNewData(propertyAccess[0],propertyAccess[1],e.target.value) this.setState({list:newData}) }; render() { return ( <div> {this.state.list.map(item => ( <Category id={item.catName} name={item.catName} key={item.catName} list={item} handleChange={this.handleChange} /> ))} </div> ); } } export default Main;
Mettez à jour votre code comme suit
import React, { Component } from "react"; import Category from "./Category"; import sampleData from "./sampleData"; class Main extends Component { constructor(props) { super(props); this.state = { list: sampleData }; } handleChange = (e, fieldName, catName) => { //get list from state const { list } = this.state //this returns the related item's index, I assume that all cats have a unique name, otherwise you should use unique values such as IDs const targetCatIndex = list.findIndex(item => item.catName === catName) //find related field index const targetFieldIndex = list[targetCatIndex].fields.findIndex(item => item.name === fieldName) //update the field and assign to state list[targetCatIndex].fields[targetFieldIndex].amount = e.target.value this.setState({ list: list }); }; render() { return ( <div> {this.state.list.map(item => ( <Category id={item.catName} name={item.catName} key={item.catName} list={item} handleChange={this.handleChange} /> ))} </div> ); } } export default Main; import React from "react"; import Item from "./Item"; const Category = ({ name, list, handleChange }) => { return ( <div className="section"> <h3>{name}</h3> {list.fields.map(item => ( <Item id={item.name} name={item.name} key={item.name} list={item} // pass field and cat referance with input event handleChange={(e, fieldName) => handleChange(e, fieldName, name) } /> ))} </div> ); }; export default Category; import React from "react"; const Item = ({ list, handleChange }) => { return ( <div className="item"> <label className="label">{list.name}</label> <input name={list.name} id={list.name} className="input" type="text" //pass related field referance here onChange={(e) => handleChange(e, list.name)} value={list.amount} /> </div> ); }; export default Item;
Et voici la démonstration fonctionnelle a>
Transmettez la catégorie et l'index des articles à votre fonction handleChange
. Utilisez ces index pour mettre à jour l'élément correct dans le tableau. Évitez la mutation d'état en ne faisant pas la
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script> <div id="root"></div> <script type="text/babel"> const Item = ({ list, handleChange, categoryIndex, itemIndex, value }) => { return ( <div className="item"> <label className="label">{list.name}</label> <input name={list.name} id={list.name} className="input" type="text" value={value} onChange={(e) => handleChange(e, categoryIndex, itemIndex)} /> </div> ); }; const Category = ({ name, list, handleChange, categoryIndex }) => { return ( <div className="section"> <h3>{name}</h3> {list.fields.map((item, index) => ( <Item id={item.name} name={item.name} key={item.name} list={item} categoryIndex={categoryIndex} itemIndex={index} value={item.amount} handleChange={handleChange} /> ))} </div> ); }; class App extends React.Component { constructor() { super(); this.state = { name: 'React', show: false, list: [ { "catName": "Category 1", "fields": [ { "name": "field 1", "amount": "0" }, { "name": "field 2", "amount": "0" } ] }, { "catName": "Category 2", "fields": [ { "name": "field 1", "amount": "0" }, { "name": "field 2", "amount": "0" } ] } ] }; } handleChange = (e, categoryIndex, itemIndex) => { const { list } = this.state; const fields = [...list[categoryIndex].fields.slice(0, itemIndex), Object.assign({}, list[categoryIndex].fields[itemIndex], { amount: e.target.value }), ...list[categoryIndex].fields.slice(itemIndex + 1) ] this.setState({ list: [...list.slice(0, categoryIndex), Object.assign({}, list[categoryIndex], { fields }), ...list.slice(categoryIndex + 1) ] }) } show = () => { this.setState({ show: true }) } render() { return ( <div> {this.state.list.map((item, index) => ( <Category id={item.catName} name={item.catName} key={item.catName} categoryIndex={index} list={item} handleChange={this.handleChange} /> ))} <br /> <button onClick={this.show}>Show changes</button> {this.state.show && <pre> {JSON.stringify(this.state.list, null, 4)} </pre> } </div> ); } } ReactDOM.render( <App />, document.getElementById('root') ); </script>
fonction handleChange
import React from "react"; import Item from "./Item"; const Category = ({ name, list, handleChange, categoryIndex }) => { return ( <div className="section"> <h3>{name}</h3> {list.fields.map((item, index) => ( <Item id={item.name} name={item.name} key={item.name} list={item} categoryIndex={categoryIndex} itemIndex={index} value={item.amount} handleChange={handleChange} /> ))} </div> ); }; export default Category;
Composant d'élément, ajoutez une catégorie et un index classé comme accessoires.
XXX
Composant de catégorie
import React from "react"; const Item = ({ list, handleChange, categoryIndex, itemIndex, value }) => { return ( <div className="item"> <label className="label">{list.name}</label> <input name={list.name} id={list.name} className="input" type="text" value={value} onChange={(e) => handleChange(e, categoryIndex, itemIndex)} /> </div> ); }; export default Item;
DEMO
handleChange = (e, categoryIndex, itemIndex) => { const { list } = this.state; const fields = [...list[categoryIndex].fields.slice(0, itemIndex), Object.assign({}, list[categoryIndex].fields[itemIndex], { amount: e.target.value }), ...list[categoryIndex].fields.slice(itemIndex + 1) ] this.setState({ list: [...list.slice(0, categoryIndex), Object.assign({}, list[categoryIndex], { fields }), ...list.slice(categoryIndex + 1) ] }) }
Merci pour vos efforts et réponse rapide, fonctionne parfaitement pour moi. Très appréciée. Avez-vous une chance d'expliquer ou de fournir des informations sur ce qui se passe exactement dans handleChange? Merci encore
J'utilise slice et object assign pour créer un nouveau tableau au lieu de changer le tableau directement. Object.assign , . slice ()
Quelle erreur obtenez vous?
OK, stackoverflow.com/help/someone-answers