Meta Code
Meta Code

Reputation: 607

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;

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!

Upvotes: 5

Views: 1236

Answers (3)

dbow
dbow

Reputation: 665

It worked for me this way. I had to import store to use dispatch in the promise.

action creator

import store from '../store/index';

export const UPLOAD_FILE = 'UPLOAD_FILE';

export function uploadTextFile(file) {
    new Promise((resolve, reject) => {
        let reader = new FileReader();
        reader.onloadend = () => { resolve(reader.result) };
        reader.readAsText(file);
    }).then(result => store.dispatch({
        type: UPLOAD_FILE,
        payload: result}
    ));
}

store

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

import rootReducer from '../reducers/index';

const store = createStore(rootReducer, applyMiddleware(thunk));
export default store;

component (partly code, redux binding)

// ...

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

export default connect(null, mapDispatchToProps)(YourComponentName);

Upvotes: 0

Tomasz Mularczyk
Tomasz Mularczyk

Reputation: 36199

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;
  }
}

Upvotes: 2

shios
shios

Reputation: 67

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 }
 }
}

Upvotes: 2

Related Questions