我目前正在Meteor中创建一个自定义React组件,用于将图像添加到列表中(以后再上传它们)。但是,当我尝试从列表中删除图像时,始终从GUI中删除最后一个元素。最初我认为这只是一个使用错误索引进行删除的简单案例,但事实证明不仅如此。
这就是我的ImageList
组件目前的样子:
import React from 'react';
import Dropzone from 'react-dropzone';
import cloneDeep from 'lodash.clonedeep';
import { ImageItem } from './image-item.js';
export class ImagesList extends React.Component {
constructor(props) {
super(props);
this.values = this.props.images || [];
this.onDrop = this.onDrop.bind(this);
this.addImages = this.addImages.bind(this);
this.deleteImage = this.deleteImage.bind(this);
this.imageChanged = this.imageChanged.bind(this);
}
onDrop(files) {
this.addImages(files);
}
onDropRejected() {
alert('Invalid file type');
}
addImages(files) {
files.forEach(file => {
this.values.push({
title: '',
description: '',
url: file.preview,
file,
});
});
this.forceUpdate();
}
deleteImage(index) {
console.log('index to delete', index);
console.log('images pre-delete', cloneDeep(this.values)); // deep-copy because logging is async
this.values.splice(index, 1);
console.log('images post-delete', cloneDeep(this.values)); // deep-copy because logging is async
this.forceUpdate();
}
imageChanged(index, image) {
this.values[index] = image;
this.forceUpdate();
}
render() {
console.log('--------RENDER--------');
return (
<div className="image-list">
<div className="list-group">
{this.values.length === 0 ?
<div className="list-group-item">
No images
</div>
:
this.values.map((image, index) => {
console.log('rendering image', image);
return (
<ImageItem
key={index}
image={image}
onDelete={() => { this.deleteImage(index); }}
onChange={(item) => { this.imageChanged(index, item); }}
deletable={true}
/>
);
})
}
</div>
<Dropzone
multiple={true}
onDrop={this.onDrop}
onDropRejected={this.onDropRejected}
className="dropzone"
activeClassName="dropzone-accept"
rejectStyle={this.rejectStyle}
accept={'image/*'}
>
<span>Drop files here</span>
</Dropzone>
</div>
);
}
}
可以使用在渲染期间使用的某些值(为了调试)初始化ImagesList
组件。例如:
<ImagesList images={[
{ title: 'Image 1', description: 'Image 1 description', url: 'http://cssdeck.com/uploads/media/items/3/3yiC6Yq.jpg' },
{ title: 'Image 2', description: 'Image 2 description', url: 'http://cssdeck.com/uploads/media/items/4/40Ly3VB.jpg' },
{ title: 'Image 3', description: 'Image 3 description', url: 'http://cssdeck.com/uploads/media/items/0/00kih8g.jpg' },
]}/>
ImagesList
为每个图片呈现ImageItem
个组件。这就是这个组件的样子:
import React from 'react';
import { RIEInput, RIETextArea } from 'riek';
export class ImageItem extends React.Component {
constructor(props) {
super(props);
this.placeholder = {
title: 'Title',
description: 'Description',
};
this.value = this.props.image;
}
render() {
return (
<div className="list-group-item">
<div className="text-content">
<h4>
<RIEInput
className="description"
value={this.value.title.length <= 0 ?
this.placeholder.title : this.value.title}
change={(item) => {
this.value.title = item.value;
this.props.onChange(this.value);
}}
validate={(value) => value.length >= 1}
classEditing="form-control"
propName="value"
/>
</h4>
<span>
<RIETextArea
className="description"
value={this.value.description.length <= 0 ?
this.placeholder.description : this.value.description}
change={(item) => {
this.value.description = item.value;
this.props.onChange(this.value);
}}
validate={(value) => value.length >= 1}
classEditing="form-control"
propName="value"
rows="2"
/>
</span>
</div>
<img className="thumb img-responsive"
style={{width: '20%' }}
src={this.value.url}
alt="Image"
data-action="zoom"
/>
{this.props.deletable ?
<div className="delete-btn">
<span onClick={this.props.onDelete}>
×
</span>
</div>
:
undefined }
</div>
);
}
}
假设我有三张图片,图片A,B和C,我想删除图片B.按下删除按钮后,图片C 将从GUI中消失
在deleteImage()
ImagesList
函数内,我正在记录要删除的索引,并记录删除前后的值。记录的索引是正确的,在这种情况下是索引1
。在删除之前,值是图像A,B和C.删除后,值应为图像A和C,因为它们应该是。
我决定在render()
的{{1}}函数中进行一些日志记录。不幸的是,这也会记录正确的值A和C,但实际上会呈现 A和B 。
我还尝试对此组件使用React状态,而不是将其与ImagesList
一起存储在局部变量中。
我尝试过的另一件事是使用Chrome的React Developer Tools插件。 Devtools也显示正确的值,但GUI仍然没有,如this screenshot所示。
我目前不知道该尝试什么,任何帮助都将不胜感激! 使用我提供的代码段,您应该能够创建一个Meteor项目并重现此错误。
答案 0 :(得分:2)
根据MasterAM的建议,我设法找到了两种不同的解决方案。
A。) 使用componentWillUpdate()
this.value
变量仅在ImageItem
组件的构造函数中设置一次。要确保正确委派更改,您必须更新this.value
函数中的componentWillUpdate()
。类似的东西:
componentWillUpdate(nextProps, nextState) {
this.value = nextProps.image;
}
B。) 直接使用该属性
这绝对是更合适的解决方案。在这里,我们摆脱了this.value
组件的构造函数中的局部变量ImageItem
。
在render()
功能中,您将this.value
替换为this.props.image
。现在没有必须使用componentWillUpdate()
功能,一切都按预期工作。