1

So, I have a function, which convert images to base64. This function is asynchronous, and it converts 4 images with the help of Promise.all(), and then I return object with received strings. So, I export async function. Here is the code:

import IMAC from '../assets/Images/devices/mac_monitor.png';
import MACBOOK from '../assets/Images/devices/macbook_pro.png';
import IPHONE_8 from '../assets/Images/devices/iphone_8.png';
import MSI_LAPTOP from '../assets/Images/devices/msi_laptop.png';

function loadImage(img) {
    return new Promise((resolve, reject) => {
        toDataURL(img, function (dataUrl) {
            resolve(dataUrl);
        })
    });
}

function toDataURL(url, callback) {
    const xhr = new XMLHttpRequest();
    xhr.onload = function () {
        let reader = new FileReader();
        reader.onloadend = function () {
            callback(reader.result);
        };
        reader.readAsDataURL(xhr.response);
    };
    xhr.open('GET', url);
    xhr.responseType = 'blob';
    xhr.send();
}

const IMAC_IMG_BASE64 = loadImage(IMAC);
const MACBOOK_IMG_BASE64 = loadImage(MACBOOK);
const MSI_IMG_BASE64 = loadImage(MSI_LAPTOP);
const PHONE_IMG_BASE64 = loadImage(IPHONE_8);

export async function loadAllImages() {
    const result = await Promise.all([IMAC_IMG_BASE64, MACBOOK_IMG_BASE64, MSI_IMG_BASE64, PHONE_IMG_BASE64]);
    return [
        {
            id: 0,
            device: "Apple iMac",
            image: result[0],
            styles: {
                carousel_item: {
                    width: "41.6vw",
                    height: "auto",
                    top: "-4.095vw",
                    left: "-0.13vw"
                },
                carousel: {
                    height: "38vw",
                    margin: "50px 0"
                },
                device: {
                    width: "46.5vw",
                    height: "38vw",
                    marginLeft: "-23.25vw"
                }
            }
        },
        {
            id: 1,
            device: "Apple Macbook Pro",
            image: result[1],
            styles: {
                carousel_item: {
                    width: "37vw",
                    height: "auto",
                    top: "-4.4vw",
                    left: ".6vw"
                },
                carousel: {
                    height: "38vw",
                    margin: "50px 0"
                },
                device: {
                    width: "55vw",
                    height: "30vw",
                    marginLeft: "-27.5vw"
                }
            }
        },
        {
            id: 2,
            device: "MSI GP72VR 7RFX",
            image: result[2],
            styles: {
                carousel_item: {
                    width: "35vw",
                    height: "auto",
                    top: "-5.8vw",
                    left: ".5vw"
                },
                carousel: {
                    height: "38vw",
                    margin: "50px 0"
                },
                device: {
                    width: "50vw",
                    height: "34vw",
                    marginLeft: "-25vw"
                }
            }
        },
        {
            id: 3,
            device: "Iphone 8",
            image: result[3],
            styles: {
                carousel_item: {
                    width: "14vw",
                    height: "auto",
                    top: "-8.2vw",
                    left: "0"
                },
                carousel: {
                    height: "38vw",
                    margin: "50px 0"
                },
                device: {
                    width: "17.7vw",
                    height: "34vw",
                    marginLeft: "-8.85vw"
                }
            }
        },
    ];
}

Then, I have this action creator, which is async, where I received data from this function (loadAllImages()), and then I call dispatch (p.s. - I am using redux-thunk)

export const loadConfigs = () => async dispatch => {
 const data = await loadAllImages();
 dispatch({type: "LOAD_DATA", payload: data});
};

Also, I have reducer, where I return payload with the object, received from the called dispatch

export default (sliderConfig = null, action) => {
    const {type, payload} = action;
    switch(type){
        case "LOAD_DATA":
            return payload;
    }

    return sliderConfig;
}

Inside the main container App.js, I call this AC inside the componentDidMount() (Don't look at fetchUser(), it does not matter in this context)

 componentDidMount() {
        this.props.fetchUser();
        this.props.loadConfigs();
    }

And, then I have component, where I am using this data, which asynchronously received from the AC. (Do not look at appDesign(), it does not matter in this context)

import React, {Component, PureComponent} from 'react';
import appDesign from '../../../decorators/scroll_resize_decorator';
import Slider from './Slider';
import {connect} from 'react-redux';
import * as actions from '../../../actions';

//Hint: Use container for the images in the slider
//Because errors with movement is appeared
class BlockFour extends Component {

    render() {

        if (this.props.sliderElements) {
            const {sliderElements, width, config, selectConfig} = this.props;
            return (
                <div className="blockfive">
                    <div className="blockfive--inner">
                        <div className="blockfive__container">
                            <div className="blockfive__container__header">
                                <div className="blockfive__container__header__container">
                                    <h1>Application Gallery</h1>
                                    <p>
                                        Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                                        A aliquid blanditiis consequuntur debitis deserunt eaque eligendi
                                    </p>
                                    <div className="blockfive__header--divider"></div>
                                </div>
                            </div>
                            <div className="blockfive__container__device">
                                <h2>
                                    Choose your device to what screenshots
                                </h2>
                                <ul className="tabs">
                                    {
                                            sliderElements.map(item =>
                                            <li
                                                key={item.id}
                                                className="tab"
                                                >
                                                <a href="#"
                                                   onClick={
                                                       () => selectConfig(item.id)
                                                   }
                                                >
                                                    {item.device}
                                                </a>
                                            </li>
                                        )
                                    }
                                </ul>
                            </div>
                            <div className="blockfive__container__gallery">
                                {
                                        <Slider
                                        width={width}
                                        styles={sliderElements[config].styles}
                                        device_image={sliderElements[config].image}
                                    />
                                }
                            </div>
                        </div>
                    </div>
                </div>
            );
        }

        return null
    }
}

const mapStateToProps = ({sliderElements, config}) => {
    return {
        sliderElements,
        config
    }
};

export default connect(mapStateToProps, actions)(appDesign(BlockFour));

So, this syntax is working, everything is loading and working. So, I have a question: What is the right way to fetch async data in AC, then pass them to reducer and then load inside the component. I don't want to use if statement inside my component.

I read couple of guides about async/await AC and how use them, but I do not completely understand how to use it in my situation. Could you please give me a brief direction how to implement it here. Thank you!

14
  • you make a request (fetch or whatever library you are going to use) in the callback of that request you have the data.. then you dispatch that data to the reducer Commented Oct 14, 2017 at 19:48
  • Thats pretty much what you're doing. No prob at all. Commented Oct 14, 2017 at 19:49
  • @WilomGfx It works, yes, but I read some guides, where people write three AC, that is DATA_IS_FETCHING, DATA_IS_FETCHED, and DATA_IS_LOADED. And I don't understand this approached, and should I use it here Commented Oct 14, 2017 at 19:51
  • 2
    @Remzes i prefer DATA_IS_FETCHING , DATA_FETCHED AND DATA_FETCH_FAIL, thats all i need. To know when these 3 happen. Commented Oct 14, 2017 at 19:53
  • 1
    @WilomGfx Great, thanks Commented Oct 14, 2017 at 20:00

2 Answers 2

1

I personally and most people follow this approach. Its completely personal and wont change much in your app, but might make your life easier.

{ type: 'FETCH_POSTS_REQUEST' }
{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }

This way your UI and other parts of your app connected to the store can act accordingly depending on the state.

Exemples includes : showing a loading icon or message when FETCH_SMTH_REQUEST is fired and your state changes to fetching and showing an error when FETCH_SMTH_FAILURE and you get the error in your state.

Sign up to request clarification or add additional context in comments.

Comments

1

I personally prefer to use constants with the same name of the content, as a Type. Like this:

export const Types = {
  FETCH_DATA_START: "FETCH_DATA_START",
  FETCH_DATA_SUCCESS: "FETCH_DATA_SUCCESS",
  FETCH_DATA_FAIL: "FETCH_DATA_FAIL",
};

Just to be clear, I've hadn't seen nothing wrong and different of it in other projects that I've worked on. At least in my opinion, your action is great. I probably would wrap it all in a try...catch clause to have a best control of my flux of data.

Just to know, if you need, when you using redux-thunk, you have the actual state as a second parameter and, if you need, you can pass extra arguments in the middleware configuration as the third parameter, like an API for exemple. So, your code, could seem like this:

export const fetchMySpecialData = () => async(dispatch, getState, API) => {
  try {
    dispatch({type: "FETCH_DATA_START"});    
    const data = await API.fetchData();
    dispatch({type: "FETCH_DATA_SUCCESS", payload: data});
  } catch (err) {
    dispatch({type: "FETCH_DATA_FAIL", error: err});
  }
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.