如何设置映射输入的嵌套 JSON 数组对象的状态
Posted
技术标签:
【中文标题】如何设置映射输入的嵌套 JSON 数组对象的状态【英文标题】:How to setState of nested JSON array object of mapped input 【发布时间】:2019-10-24 03:36:40 【问题描述】:我有一个JSON
文件,其中包含多个类别,每个类别都有一个名称,其中包含一组具有自己名称和值的输入字段。
如何使用setState
更新每个onChange
的值字段?类别和字段使用map()
呈现。
我可以让它在没有嵌套字段的情况下工作,但不能。感谢任何帮助。
JSON 文件
[
"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.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
;
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;
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=item.name
name=item.name
key=item.name
list=item
handleChange=handleChange
/>
))
</div>
);
;
export default Category;
Item.js
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;
【问题讨论】:
你遇到了什么错误? 好的,***.com/help/someone-answers 【参考方案1】:将类别和项目索引传递给您的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)
]
)
Item 组件,添加类别和归档索引作为道具。
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>
【讨论】:
感谢您的努力和快速响应,非常适合我。非常感激。您是否有机会解释或提供有关 handleChange 中到底发生了什么的背景?再次感谢 我正在使用切片和对象分配来创建一个新数组,而不是直接更改数组。 Object.assign, .slice()【参考方案2】:您的 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
/>
))
还要确保在主类的构造函数中设置一个空数组:
constructor(props)
super(props);
this.state =
list: []
;
【讨论】:
【参考方案3】:如下更新你的代码
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;
这里是working demo
【讨论】:
【参考方案4】:让我们从下往上开始
您需要通过以下方式向 Item.js 提供其父类别的 ID
将 id 更改为id=$name,$item.name
。也可以添加 onClick 事件来清理以前的数据
类别组件需要提供给item组件的id
然后在主组件中获得对 json 的正确访问权限后,您可以使用 createNewData 方法创建新对象这是结果:
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;
【讨论】:
以上是关于如何设置映射输入的嵌套 JSON 数组对象的状态的主要内容,如果未能解决你的问题,请参考以下文章