在 Redux thunk 中创建的数组未正确传递到状态树
Posted
技术标签:
【中文标题】在 Redux thunk 中创建的数组未正确传递到状态树【英文标题】:Array created in a Redux thunk is not being passed properly to the State tree 【发布时间】:2017-11-05 05:06:34 【问题描述】:我正在构建一个 React/Redux 项目,该项目对 S3 存储桶进行几次调用以获取图像,然后将它们渲染到应用程序中。
我发现由于某种原因,我无法遍历我在状态树中设置的数组,因为这些调用将它们呈现到应用程序上。我相信我可能正在处理一个类似数组的对象,或者我创建的 Promise 中的某些东西导致了这种突变的发生。
首先,我将这个文件放在一个名为 utils 的文件夹中:
const bucketName = 'foo';
const roleARN = 'arn:aws:s3:::foo';
const identityPoolId = 'us-west-2:anActualIdHere';
AWS.config.update(
region: 'us-west-2',
credentials: new AWS.CognitoIdentityCredentials(
IdentityPoolId: identityPoolId
)
);
const bucket = new AWS.S3(
params:
Bucket: bucketName,
);
const textDecoder = new TextDecoder('utf8');
export function listObjects (callback)
return bucket.listObjects((error, data) =>
if (error)
console.error('error: ', error);
return;
callback(data.Contents);
);
export function getSingleObject (key)
let getObject = new Promise((resolve, reject) =>
bucket.getObject(
Bucket: bucketName,
Key: key
, (error, data) =>
if (error)
console.error('error: ', error);
resolve(data.Body.toString('base64'));
);
)
return getObject.then((result) =>
return result;
)
这里发生的情况是listObjects
将返回特定 S3 存储桶中所有项目的数组。
然后,getSingleObject
函数根据所有项目列表中提供的键获取单个对象的内容,获取其 Uint8Array 并将其转换为 base-64 字符串。
这两个函数在一个 thunk 动作中被调用:
import listObjects, getSingleObject from '../utils/index.js';
export function loadPhotos ()
return function (dispatch)
listObjects((results) =>
let photos = [];
let mapPhotosToArray = new Promise((resolve, reject) =>
results.forEach((singlePhoto) =>
let getSinglePhoto = new Promise((resolve, reject) =>
resolve(getSingleObject(singlePhoto.Key));
);
getSinglePhoto.then((result) =>
photos.push(result);
);
);
resolve(photos);
)
mapPhotosToArray.then((result) =>
dispatch(savePhotos(result));
);
);
function savePhotos (photos)
return
type: 'GET_PHOTOS',
photos
loadPhotos
是暴露给我的 Redux 容器的 thunk 操作。它返回一个函数,该函数首先调用 utils 文件中的 listObjects
函数,并传递给它一个回调,该回调会创建一个名为 photos
的数组。
然后,它创建一个新的 Promise 循环遍历 listObjects
实用函数返回的结果数组。在此结果数组的每次迭代中,我都会实例化另一个调用 getSingleObject
实用程序的新 Promise。
在该迭代中,我将getSingleObject
的结果推送到在传递给listObjects
的回调中创建的photos
数组中。
我在loadPhotos
thunk 动作中做的最后一件事是调用外部 Promise,然后将结果分派给 savePhotos
动作,后者最终将有效负载对象分派到 store 以供我的 reducer 捕获。
这是我的 reducer 的外观:
const defaultState =
photos: []
const photos = (state = defaultState, action) =>
switch (action.type)
case 'GET_PHOTOS':
return Object.assign(, state,
photos: action.photos
)
default:
return state;
;
export default photos;
这是我设置应用程序入口点的方式:
import React from 'react';
import ReactDOM from 'react-dom';
import Provider from 'react-redux';
import createStore, compose, combineReducers, applyMiddleware from 'redux';
import thunk from 'redux-thunk';
import photos from './reducers/';
import App from './components/app';
import styles from './styles/styles.css';
const createStoreWithMiddleware = compose(applyMiddleware(thunk))(createStore);
const store = createStoreWithMiddleware(photos, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
ReactDOM.render(
<Provider store= store >
<App />
</Provider>,
document.getElementById('root')
)
这是呈现的 App 组件:
import React, Component from 'react';
import ReactDOM from 'react-dom';
import AllPhotos from '../containers/AllPhotos';
import Navbar from './Navbar';
import '../styles/styles.css';
export default class App extends Component
constructor (props)
super (props);
render ()
return (
<div className="app">
<Navbar />
<AllPhotos />
</div>
)
这是它渲染的 AllPhotos 容器:
import connect from 'react-redux';
import AllPhotos from '../components/AllPhotos';
import
loadPhotos
from '../actions/index.js';
const mapDispatchToProps = (dispatch) =>
return
loadPhotos: () =>
dispatch(loadPhotos());
const mapStateToProps = (state) =>
return
photos: state.photos
export default connect(mapStateToProps, mapDispatchToProps)(AllPhotos);
最后,这是 AllPhotos 组件:
import React, Component from 'react';
import _ from 'lodash';
export default class AllPhotos extends Component
constructor(props)
super(props);
componentWillMount ()
this.props.loadPhotos();
render ()
return (
<div>
<h1>Test</h1>
this._renderImages()
</div>
)
_renderImages ()
_.map(this.props.photos, (image) =>
return (
<img
src= 'data:image/png;base64,' + image
width= 480
/>
)
)
当我尝试在_renderImages
中记录this.props.photos
时会发生这种情况:
第一个日志发生在照片数组被填充并加载到我的状态之前。
但是,如果我要在仅记录数组本身的同一区域中记录 this.props.photos
的长度,这就是我所看到的:
当我在同一行在this.props.photos
上调用 Array.isArray 时,我得到的是:
我也尝试使用Array.from
将其转换为数组,但没有成功。
再深入一点,我试图从减速器中的操作负载中找到photos
数组的长度,但仍然收到0
作为输出。我也在savePhotos
操作中尝试了这个,并发现了相同的结果。
因此,我相信我可能没有正确编写我的 Promise。有人可以帮我指出正确的方向吗?
【问题讨论】:
你应该在componentDidMount中获取你的数据:facebook.github.io/react/docs/… 【参考方案1】:您可以尝试重写 listObjects
和 getSingleObject
以返回 Promises 并使用 Promise.all。
那你可以写loadPhotos
export function loadPhotos ()
return function (dispatch)
listObjects().then((results) =>
Promise.all(results.map((singlePhoto) => getSingleObject(singlePhoto.Key)))
.then((result) => dispatch(savePhotos(result)))
)
【讨论】:
以上是关于在 Redux thunk 中创建的数组未正确传递到状态树的主要内容,如果未能解决你的问题,请参考以下文章
使用 Redux、Thunk、Axios 创建多部分表单组件
使用 Redux Thunk 时正确理解 useEffect 中的依赖数组
Reducer 返回未定义(Redux-Thunk 和 Promise)
如何在 Typescript 中使用 redux-thunk 使用 ThunkAction 正确键入 thunk?
无法使用 next-redux-wrapper 让 Redux (Thunks) 与 NextJS 一起工作:未调用 getInitalProps