在Redux中异步/等待

时间:2017-10-14 19:46:44

标签: reactjs asynchronous promise redux redux-thunk

所以,我有一个函数,它将图像转换为base64。这个函数是异步的,它在Promise.all()的帮助下转换了4个图像,然后我返回带有接收字符串的对象。所以,我导出异步功能。这是代码:

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"
                }
            }
        },
    ];
}

然后,我有这个动作创建者,它是异步的,我从这个函数接收数据(loadAllImages()),然后我调用dispatch(p.s. - 我正在使用redux-thunk)

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

另外,我有reducer,在那里我返回带有对象的有效负载,从被叫调度

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

    return sliderConfig;
}

在主容器App.js中,我在componentDidMount()中调用此AC (不要看fetchUser(),在这种情况下无关紧要)

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

然后,我有组件,我正在使用这个数据,从AC异步接收。 (不要看appDesign(),在这种情况下无关紧要)

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

所以,这种语法很有效,一切都在加载和工作。所以,我有一个问题:在AC中获取异步数据的正确方法是什么,然后将它们传递给reducer,然后在组件内部加载。我不想在我的组件中使用if语句。

我阅读了几本关于async / await AC的指南以及如何使用它们,但我并不完全理解如何在我的情况下使用它。能否请您简要介绍一下如何在此实施。谢谢!

2 个答案:

答案 0 :(得分:1)

我个人和大多数人都遵循此approach。它完全是个人化的,在您的应用中不会发生太大变化,但可能会让您的生活更轻松。

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

这样,您的用户界面和连接到商店的应用的其他部分可以根据状态进行相应操作。

例子包括:在FETCH_SMTH_REQUEST被触发时显示加载图标或消息,并且您的状态更改为抓取并在FETCH_SMTH_FAILURE时显示错误,并且您的状态为error。< / p>

答案 1 :(得分:1)

我个人更喜欢使用与内容具有相同名称的常量作为Type。像这样:

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

请明确一点,我没有发现任何错误,在我从事的其他项目中也没有什么不同。至少在我看来,您的行动很棒。我可能会将所有内容包装在try ... catch子句中,以最好地控制我的数据流量。

只要知道,如果需要,在使用redux-thunk时,您会将实际状态作为第二个参数,并且,如果需要,可以在中间件配置中传递额外的参数作为第三个参数,例如例如一个API。因此,您的代码可能看起来像这样:

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