Redux 中的异步/等待

Posted

技术标签:

【中文标题】Redux 中的异步/等待【英文标题】:Async/Await in Redux 【发布时间】:2018-03-26 16:43:04 【问题描述】:

所以,我有一个函数,可以将图像转换为 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 以及如何使用它们的指南,但我并不完全了解如何在我的情况下使用它。你能否给我一个简短的指导如何在这里实现它。谢谢!

【问题讨论】:

您在该请求的回调中发出请求(获取或您将要使用的任何库)您有数据..然后您将该数据发送到减速器 这就是你正在做的事情。一点问题都没有。 @WilomGfx 它可以工作,是的,但我读了一些指南,人们在其中写了三个 AC,即 DATA_IS_FETCHING、DATA_IS_FETCHED 和 DATA_IS_LOADED。而且我不明白这种方法,我应该在这里使用它 @Remzes 我更喜欢 DATA_IS_FETCHING、DATA_FETCHED 和 DATA_FETCH_FAIL,这就是我所需要的。要知道这 3 种情况何时发生。 @WilomGfx 太好了,谢谢 【参考方案1】:

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

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

这样,您的 UI 和连接到商店的应用的其他部分可以根据状态采取相应的行动。

示例包括:当FETCH_SMTH_REQUEST 被触发并且您的状态更改为正在获取时显示加载图标或消息,并在FETCH_SMTH_FAILURE 并且您在您的状态中获得error 时显示错误。

【讨论】:

【参考方案2】:

我个人更喜欢使用与内容同名的常量,作为 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);
  
;

【讨论】:

以上是关于Redux 中的异步/等待的主要内容,如果未能解决你的问题,请参考以下文章

Redux 等待异步 thunk 继续进行

Redux等待异步thunk继续前进

Redux thunk:如何等待异步操作的完成

反应等待异步还是重新渲染?

Redux 和 isFetching 中的顺序异步操作

为啥我们需要中间件用于 Redux 中的异步流?