Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to integrate FileReader in Redux?

I commented that I was reading the official documentation a lot, and I fully understand the concept and functioning of this. The problem is that it costs me a bit more to apply it in reality. So I started to practice and I came across something that puzzled me, I want to create a button that loads an image and shows the preview of it. However, this consists of some steps such as;

  • verify that the image has been uploaded correctly
  • create a fileReader
  • update the status of my application (path, and name of the image)
  • show the image

This would look like this:

onImageChange(event) {
    if (event.target.files && event.target.files[0]) {
        let reader = new FileReader();
        let file = event.target.files[0];

        reader.onloadend = () => {
            this.setState({
                imageFile: file,
                imageName: file.name
            });
        }   
        reader.readAsDataURL(file)
    }
}

I tried to create an action and that the payload is image Name and imageFile but I do not know where to put the reader ...

this is my component currently:

import React, { Component } from "react";
import ImageModalForm from "../../../Commons/components/ImageModalForm";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import { loadImage } from "../../actions";

const faceImage = require("../../../../img/face5.gif");

class ThumbnailComp extends Component {
  onImageChange(event) {
    if (event.target.files && event.target.files[0]) {
      let reader = new FileReader();
      let file = event.target.files[0];

      reader.onloadend = () => {
        this.setState({
          imageFile: file,
          imageName: reader.result
        });
      };
      reader.readAsDataURL(file);
    }
  }

  render() {
    return (
      <ImageModalForm
        imageFile={this.props.imageFile}
        imageName={this.props.imageName}
        onImageChange={this.onImageChange}
      />
    );
  }
}

function mapStateToProps(state) {
  return {
    image: state.image
  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ loadImage }, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(ThumbnailComp);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Any correction is welcome ... Thanks!

like image 494
Meta Code Avatar asked May 26 '18 16:05

Meta Code


2 Answers

Why don't you just pass result from FileReader to action creator?

onImageChange(event) {
    if (event.target.files && event.target.files[0]) {
      let reader = new FileReader();
      let file = event.target.files[0];

      reader.onloadend = () => {
        this.props.loadImage(reader.result)
      };
      reader.readAsDataURL(file);
    }
}

// action creator
const loadImage = (result) => ({ type: LOAD_IMAGE, result })

side note - you can't pass events to redux as SyntheticEvents gets nullified.

However, if you want to make this outside of component you need to do it in action creator - don't do it in reducer as they need to be pure without any side-effects. You will also need some kind of middleware to do it - the simplest yet enough for this would be redux-thunk - which allows you to return a function from your action creators:

onImageChange(event) {
    if (event.target.files && event.target.files[0]) {
      let file = event.target.files[0];
      this.props.loadImage(file)
    }
}

// action creator
const loadImage = file => dispatch => {
  let reader = new FileReader();

  reader.onloadend = () => {
    dispatch({
      type: 'SET_IMAGE_IN_REDUCER',
      image: reader.result
    });
  };
  reader.readAsDataURL(file);
}


// example reducer
const initialState = { image: null };
function reducer(state = initialState, action) {
  switch(action.type) {
    case 'SET_IMAGE_IN_REDUCER': 
      return { ...state, image: action.image }
    ...
    default:
      return state;
  }
}
like image 180
Tomasz Mularczyk Avatar answered Nov 08 '22 18:11

Tomasz Mularczyk


I have a similar problem, I would do it that way. But I'm not sure...

action.js

function loadImage(event){
  if (event.target.files && event.target.files[0]){
   let file = event.target.files[0];
   let fileName = file.name
  }
  return {
   type: LOAD_IMAGE,
   payload: { imageFile: file, imageFileName: fileName }
 }
}
like image 42
shios Avatar answered Nov 08 '22 18:11

shios