2016-11-04 10 views
0

현재 이미지를 목록에 추가 한 다음 나중에 업로드하기 위해 Meteor에 사용자 지정 React 구성 요소를 만들고 있습니다. 그러나 목록에서 이미지를 삭제하려고 할 때 항상 마지막 요소가 GUI에서 제거됩니다. 처음에는 이것이 잘못된 색인을 삭제에 사용하는 단순한 경우라고 생각했지만 그 이상의 것으로 밝혀졌습니다.렌더링 된 HTML이 React 구성 요소와 동기화되지 않았습니다.

이처럼 내 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}> 
       &times; 
      </span> 
      </div> 
      : 
     undefined } 
     </div> 
    ); 
    } 
} 

의 내가 세 가지 이미지, 이미지 A, B 및 C가, 내가, 이미지 C이 사라집니다 삭제 버튼을 누른 후 이미지 B를 삭제하고 싶은 말은하자 GUI 대신.

deleteImage() 함수 내에서 ImagesList의 인덱스를 로깅하고 삭제 전후에 값을 기록합니다. 로그 된 인덱스는 올 Y 른 것으로,이 경우 인덱스는 1입니다. 삭제하기 전에 값은 이미지 A, B 및 C입니다. 삭제 후 값은 이미지 A 및 C입니다.

ImagesListrender() 기능 내에서 일부 로깅을 결정했습니다. 불행히도 이것은 또한 정확한 값 A와 C를 기록하지만, A와 B이 실제로 렌더링됩니다.

또한 로컬 변수에 forceUpdate()과 함께 저장하는 대신이 구성 요소에 React 상태를 사용하려고했습니다.

내가 시도한 또 다른 사항은 Chrome 용 React Developer Tools 플러그인을 사용하는 것입니다. Devtools도 올바른 값을 표시하지만 GUI는 여전히 this screenshot과 같이 표시되지 않습니다.

나는 무엇을 시도 할 지에 대한 아이디어가 없으므로 도움을 받으실 수 있습니다! 제공된 스 니펫을 사용하면 Meteor 프로젝트를 생성하고이 버그를 재현 할 수 있습니다.

+1

일반적으로 이미지 원본의 이미지를 변경해야합니다. 그들이 소품으로 당신에게 주어 졌기 때문에, 그 변화는 구성 요소 자체에서 일어나서는 안됩니다. 이는 대개 액션을 트리거 한 다음 전역 상태 관리자를 사용하여'props '에 매핑 된 상태를 업데이트함으로써 수행됩니다. 귀하의 질문에 시연하는 방식으로 구성 요소 업데이트를 어디에서 보았는지 궁금합니다. – MasterAM

답변

2

MasterAM의 제안을 통해 두 가지 솔루션을 찾을 수있었습니다.


A.)componentWillUpdate()

this.value 변수는 ImageItem 성분의 생성자, 즉 한 번만 사용 설정. 변경 사항을 올바르게 위임하려면 함수 내에서 this.value을 업데이트해야합니다. 뭔가 같은 :

componentWillUpdate(nextProps, nextState) { 
    this.value = nextProps.image; 
} 

B.)직접 재산을 사용하는 것

이것은 분명히 더 적절한 해결책입니다. 여기서 우리는 ImageItem 구성 요소의 생성자 안에 지역 변수 this.value을 제거합니다.

render() 함수 안에서 this.valuethis.props.image으로 바꿉니다. 이제 없이을 사용하면 componentWillUpdate() 기능을 사용해야하므로 모든 것이 예상대로 작동합니다.

관련 문제