Reactjs 子组件正确删除父组件';s州';s对象条目,但在重新渲染时卸载了错误的子对象
我有一个Reactjs 子组件正确删除父组件';s州';s对象条目,但在重新渲染时卸载了错误的子对象,reactjs,rerender,Reactjs,Rerender,我有一个Order组件,它将一个Javascript对象保存在this.state.newItems中,并为每个子对象呈现OrderItem组件 OrderItem还接收回调以对父级状态进行操作 我得到一个行为,比如javascript对象在删除时被正确更新,但卸载了错误的节点 视频: 如您所见,卸载了错误的OrderItem组件 相关代码: export default class Order extends Component { constructor (props) { s
Order
组件,它将一个Javascript对象保存在this.state.newItems
中,并为每个子对象呈现OrderItem
组件
OrderItem
还接收回调以对父级状态进行操作
我得到一个行为,比如javascript对象在删除时被正确更新,但卸载了错误的节点
视频:
如您所见,卸载了错误的OrderItem
组件
相关代码:
export default class Order extends Component {
constructor (props) {
super (props)
this.state = {
newItems: null
}
if (this.props.isNew)
this.data = null
else {
this.data = { ... this.props }
}
}
/* _commit = () => {
console.log(this.data)
if (this.data.trim().length > 0)
socket.emit('article:client:insert', { name: this.data })
} */
addNewOrderItem = async () => {
let _ = this.state.newItems ? { ... this.state.newItems } : {}
_[Date.now().toString()] = {
articleData: {
isNew: true,
unitaryPrice: 0.0
}
}
await this.setState({
newItems: _
})
console.log(this.state.newItems)
}
deleteNewOrderItem = async id => {
let _ = { ...this.state.newItems }
console.log("Deleting " + id)
delete _[id]
await this.setState({
newItems: _
})
console.log(this.state.newItems)
}
updateNewOrderItem = async (id, value) => {
let _ = { ...this.state.newItems }
_[id] = value
await this.setState({
newItems: _
})
console.log(this.state.newItems)
}
renderNewItems () {
if (!this.state.newItems) return null
let _ = []
for (let _id in this.state.newItems)
_.push(
<OrderItem
articleData={this.state.newItems[_id].articleData}
id={_id}
onUpdate={this.updateNewOrderItem}
onDelete={this.deleteNewOrderItem}
/>
)
return _
}
render () {
const { data } = this
return (
// ...
{
this.renderNewItems()
}
// ...
)
}
}
export default class OrderItem extends React.Component {
state = {
base64img: null
}
constructor (props) {
super (props)
this.setData(props)
}
componentWillUnmount () {
console.log("Will unmount " + this.props.id)
}
setData (props) {
this.data = { ...props }
delete this.data.onDelete
delete this.data.onUpdate
}
handleImageInsert = (event) => {
let objectFile = event.target.files[0]
if (!objectFile) return
let reader = new FileReader()
reader.onload = upload =>
this.setState({
base64img: upload.target.result
})
reader.readAsDataURL(objectFile)
this.data.articleData.newImage = objectFile
this.updateData()
}
handleUnitaryPriceChange = e => {
let newPrice = parseFloat(e.target.value)
if (newPrice != NaN) {
this.data.articleData.unitaryPrice = newPrice
this.updateData()
}
}
handleBriefChange = e => {
this.data.articleData.brief = e.target.value
this.updateData()
}
handleTableChange = update => {
this.data = { ... this.data, ... update }
this.updateData()
}
updateData = () => {
this.props.onUpdate(this.props.id, this.data)
}
confirmDelete = () => {
this.props.onDelete(this.props.id)
}
render () {
const { data: { articleData } } = this
return (
<div>
<input
ref={ref => this.fileUploadRef = ref}
onChange={this.handleImageInsert}
style={{ display: 'none' }}
type="file"
accept="image/*"
/>
<div className="orderTableImageColumn">
{
this.state.base64img &&
<img src={this.state.base64img} style={{width: '100%'}} />
}
<div className="orderTableImageColumnControls">
<Button
variant="raised"
className="print-hide"
color="primary"
style={{ display: 'inline-block' }}
onClick={() => this.fileUploadRef.click()}
>
<PhotoCamera />
</Button>
<Button
variant="raised"
className="print-hide"
color="primary"
style={{ display: 'inline-block', backgroundColor: 'red' }}
onClick={this.confirmDelete}
>
<Delete />
</Button>
</div>
</div>
<div
style={{
whiteSpace: 'normal',
wordWrap: 'break-word',
width: '65%',
padding: '2%',
paddingLeft: '1%',
verticalAlign: 'top',
display: 'inline-block'
}}>
<div style={{display: 'inline-block', width: '50%'}}>
{
!(articleData.isPending || articleData.isNew) && <span className='tableLabelSmall'>SKU</span>
}
<br /><br />
<textarea
name="brief"
className='articleBriefTextarea'
onChange={this.handleBriefChange}
value={articleData.brief ? articleData.brief : 'Descrizione'}
/>
</div>
<div style={{
display: 'inline-block',
width: '47%',
marginLeft: '1%',
padding: '1%',
verticalAlign: 'top',
backgroundColor: 'gold'
}}>
<span className='tableLabelSmall'>PREZZO UNITARIO €</span>
<input
type="text"
style={{padding: '5px', marginBottom: '3px'}}
oninput="this.value = this.value.replace(/[^0-9.]/g, ''); this.value = this.value.replace(/(\..*)\./g, '$1');"
name="unitaryPrice"
placeholder='0'
onChange={this.handleUnitaryPriceChange}
/>
<br />
<textarea
name="needs"
className='articleBriefTextarea'
value={articleData.needs ? articleData.needs : 'Materiali, accessori, necessità'}
/>
</div>
</div>
</div>
)
}
}
导出默认类顺序扩展组件{
建造师(道具){
超级(道具)
此.state={
newItems:null
}
如果(this.props.isNew)
this.data=null
否则{
this.data={…this.props}
}
}
/*提交=()=>{
console.log(this.data)
if(this.data.trim().length>0)
emit('article:client:insert',{name:this.data})
} */
addNewOrderItem=async()=>{
让{=this.state.newItems?{…this.state.newItems}:{}
_[Date.now().toString()]={
条款数据:{
是的,
统一价格:0.0
}
}
等待这一天({
新增项目:_
})
console.log(this.state.newItems)
}
deleteNewOrderItem=异步id=>{
让{…this.state.newItems}
console.log(“删除”+id)
删除[id]
等待这一天({
新增项目:_
})
console.log(this.state.newItems)
}
updateNewOrderItem=异步(id,值)=>{
让{…this.state.newItems}
_[id]=值
等待这一天({
新增项目:_
})
console.log(this.state.newItems)
}
renderNewItems(){
如果(!this.state.newItems)返回null
让我们
for(在此.state.newItems中设置_id)
_.推(
)
返回_
}
渲染(){
const{data}=这个
返回(
// ...
{
this.renderNewItems()
}
// ...
)
}
}
导出默认类OrderItem扩展React.Component{
状态={
base64img:null
}
建造师(道具){
超级(道具)
这个.setData(props)
}
组件将卸载(){
log(“将卸载”+此.props.id)
}
设置数据(道具){
this.data={…props}
删除此.data.onDelete
删除此.data.onUpdate
}
handleImageInsert=(事件)=>{
让objectFile=event.target.files[0]
如果(!objectFile)返回
let reader=new FileReader()
reader.onload=上传=>
这是我的国家({
base64img:upload.target.result
})
reader.readAsDataURL(objectFile)
this.data.articleData.newImage=objectFile
这是updateData()
}
handleUnitaryPriceChange=e=>{
让newPrice=parseFloat(例如target.value)
if(newPrice!=NaN){
this.data.articleData.unitaryPrice=newPrice
这是updateData()
}
}
车把更换=e=>{
this.data.articleData.brief=e.target.value
这是updateData()
}
handleTableChange=更新=>{
this.data={…this.data,…update}
这是updateData()
}
更新数据=()=>{
this.props.onUpdate(this.props.id,this.data)
}
确认删除=()=>{
this.props.onDelete(this.props.id)
}
渲染(){
const{data:{articleData}}=这个
返回(
this.fileUploadRef=ref}
onChange={this.handleImageInsert}
样式={{display:'none'}}
type=“文件”
accept=“image/*”
/>
{
this.state.base64img&&
}
this.fileUploadRef.click()}
>
{
!(articleData.isPending | | articleData.isNew)和&SKU
}
PREZZO UNITARIO€
)
}
}
您需要在
上设置键
道具,以便React知道哪些道具对应于哪个组件实例:
renderNewItems () {
if (!this.state.newItems) return null
let _ = []
for (let _id in this.state.newItems)
_.push(
<OrderItem
key={_id}
articleData={this.state.newItems[_id].articleData}
id={_id}
onUpdate={this.updateNewOrderItem}
onDelete={this.deleteNewOrderItem}
/>
)
return _
}
renderNewItems(){
如果(!this.state.newItems)返回null
让我们
for(在此.state.newItems中设置_id)
_.推(
)
返回_
}
控制台中的错误实际上也在警告您这一点
另一方面,如果不允许子组件改变父组件的状态,则更容易对代码进行推理。您需要将
键设置为
上的道具,以便React知道哪些道具对应于哪个组件实例:
renderNewItems () {
if (!this.state.newItems) return null
let _ = []
for (let _id in this.state.newItems)
_.push(
<OrderItem
key={_id}
articleData={this.state.newItems[_id].articleData}
id={_id}
onUpdate={this.updateNewOrderItem}
onDelete={this.deleteNewOrderItem}
/>
)
return _
}
renderNewItems(){
如果(!this.state.newItems)返回null
让我们
for(在此.state.newItems中设置_id)
_.推(
)
返回_
}
控制台中的错误实际上也在警告您这一点
另一方面,如果不允许子组件改变父组件的状态,那么就更容易对代码进行推理。为什么数据不存储在状态中?data
是OrderItem
道具的深层副本,因此,在该组件内部是可变的,我可以将其传递回父级的直通注入回调。为什么数据
不存储在状态中?数据
是订单项
的道具的深度副本,因此该组件内部是可变的,我可以将其传递回父级的直通注入回调