1

I am trying to implement an on-change handler for an audio file that loads the previous state of the file and records it when the user changes the file.

More concretely: When the user enters the component, the link to the file should be displayed in the form field, and when the user updates the file, the new path to the file should be recorded by the on-change handler.

Currently, the on-change handler works for the audio file. However, the previous file is not loaded, so the form field for the file remains empty when the component loads. I do not receive an error message that makes it difficult to solve the problem at this time.

The file is successfully fetched from the backend (I can load the previous state of title, and content just for the file field its not working).

export class EditStory extends Component {

  constructor(props) {
    super(props);
    this.getStory = this.getStory.bind(this);
    this.updateStory = this.updateStory.bind(this);
    this.onChangeTitle = this.onChangeTitle.bind(this);
    this.onChangeContent=this.onChangeContent.bind(this);
    this.onChangeAudio=this.onChangeAudio.bind(this);
    this.inputRef = React.createRef();


this.state = {
    story: {
        id: null,
        title: "",
        content: "",
        audio: ""
      }

    };
}


    componentDidMount() {
    this.getStory(this.props.match.params.id);
  }






  onChangeTitle(e) {
    const title = e.target.value;

    this.setState(prevState => ({
      story: {
        ...prevState.title,
        title: title
      }
    }), () => console.log(this.state));
  }


  onChangeContent(e) {
    const content = e.target.value;

    this.setState(prevState => ({
      story: {
        ...prevState.story,
        content: content
      }
    }), () => console.log(this.state));

  }


  onChangeAudio(e) {
    const audio = e.target.name;

    this.setState(prevState => ({
      story: {
        ...prevState.story,
        [e.target.name]: e.target.files[0]
      }
    }), () => console.log(this.state.audio));

  }



  getStory(id) {
    this.props.getSingleStory(id)
      .then(response => {
        this.setState({
          story: response.data
        });
        console.log(response.data);
        ;
      })
      .catch(e => {
        console.log(e);
      });
  }  



 updateStory() {
    const id = this.state.story.id;

    let title = this.state.story.title;

    let content = this.state.story.content;
    let audio = this.state.story.audio;
    let UpdatedData = new FormData();
    UpdatedData.append('audio', audio); // add audio to formData
    UpdatedData.append('title', title); // add title to formData
    UpdatedData.append('content', content);
    console.log (UpdatedData);
    this.props.editStory(
      id, 
      UpdatedData
    )
      .then(response => {
        console.log(response.data);

      })
      .catch(e => {
        console.log(e);
      });
  }





  static propTypes = {
      getSingleStory: PropTypes.func.isRequired,
      editStory: PropTypes.func.isRequired
  };


  render() {
    const {story} = this.state;
    return (
      <div>
        <h1>Edit {story.title}</h1>

          <div className="form-group">
            <label>Title</label>
            <input type="text" name="title" defaultValue={story.title} onChange={this.onChangeTitle} className="form-control" />
          </div>
          <div className="form-group">
            <label>Content</label>
            <textarea name="content" rows="5" defaultValue={story.content} onChange={this.onChangeContent} className="form-control" />
          </div>

          <div className="form-group">
            <label>Audio</label>
            <input
              className="form-control"
              type="file"
              name="audio"
              onChange={this.onChangeAudio}
              ref={this.inputRef} // refer to ref

            />
          </div>

          <div className="btn-group">
            <button type="submit" onClick={this.updateStory} className="btn btn-dark">Update</button>
            <button type="button"  className="btn btn-secondary">Cancel</button>
          </div>

      </div>
    );
  }
}


export default connect(
  null,
  { getSingleStory, editStory, }
)(EditStory);

What can be the

dan_boy
  • 1,735
  • 4
  • 19
  • 50

1 Answers1

1

You can dynamically set the value of input field by setting the file key on inputRef when you get the data from server . However you would need to convert your incoming file to FileList object using DataTransfer constructor. You can find more info for this on this post : How to set File objects and length property at FileList object where the files are also reflected at FormData object?

You updated code:

getStory(id) {
    this.props.getSingleStory(id)
      .then(response => {
        this.setState({
          story: response.data
        });
        const file =  new File(response.data.file, "filename.txt, {
           type: "text/plain",
         });
        const dT = new ClipboardEvent('').clipboardData || new DataTransfer(); 
        dT.items.addfile(file);

        this.inputRef.current.files = dT.files;
        ;
      })
      .catch(e => {
        console.log(e);
      });
  }
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • Thank you very much for the answer! Now I understand the problem better. However, somehow I cannot completely solve the problem by using your code. It is not possible to create a file from the incoming form data. `TypeError: Failed to construct 'File': The provided value cannot be converted to a sequence.` – dan_boy May 10 '20 at 07:40