我有一个包含多个类别的JSON
文件,每个类别都有一个名称,该名称带有一组具有自己的名称和值的输入字段。
如何使用setState
更新每个onChange
的值字段?类别和字段使用map()
呈现。
我能够使它不使用嵌套字段,但不能使用。感谢任何帮助。
[{
"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"
}
}]
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;
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 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;
答案 0 :(得分:0)
您的JSON无效。您还忘记了检查list
是否已经包含任何数据。
尝试一下:
在您的handleChange
方法中,请确保使用正确的JSON标记。您忘记了结局]}
:
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"
}
]}
]})
在您的Main
类的render方法中,检查列表是否为数组,并且列表的长度是否大于0。如果设置了非数组类型的值,这将防止任何渲染错误。
{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}
/>
))}
还要确保在您的Main Class的构造函数中设置一个空数组:
constructor(props) {
super(props);
this.state = {
list: []
};
}
答案 1 :(得分:0)
让我们从下而上
您需要通过以下方式向Item.js提供其父类别的ID:
将ID更改为id={${name},${item.name}}
。添加onClick事件以清除以前的数据也很不错
需要提供的类别组件是项目组件的ID
这是结果:
Main.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;
Item.js
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 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;
答案 2 :(得分:0)
如下更新代码
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;
答案 3 :(得分:0)
将类别和项目索引传递给您的handleChange
函数。使用这些索引来更新数组中的正确项目。通过不做避免状态突变
// state mutation
this.state.list[categoryIndex].fields[fieldIndex].amount = e.target.value
handleChange函数
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)
]
})
}
项目组件,添加类别和字段索引作为道具。
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;
类别组件
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;
演示
<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>