在 Redux thunk 中创建的数组未正确传递到状态树
Array created in a Redux thunk is not being passed properly to the State tree
我正在构建一个 React/Redux 项目,该项目对 S3 存储桶进行几次调用以抓取图像,然后将它们渲染到应用程序中。
我发现由于某些原因,我无法遍历我在状态树中设置的数组,因为这些调用将它们呈现到应用程序上。我相信我可能正在处理一个 类数组对象 或我创建的 Promises 中的某些东西导致了这种突变的发生。
首先,我将这个文件放在名为 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
将 return 特定 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 动作。它 return 是一个函数,它首先调用 utils 文件中的 listObjects
函数,向它传递一个回调,创建一个名为 photos
的数组。
然后,它创建一个新的 Promise,循环遍历 return 由 listObjects
效用函数编辑的结果数组。在此结果数组的每次迭代中,我实例化另一个调用 getSingleObject
实用程序的新 Promise。
在那次迭代中,我将 getSingleObject
的结果推送到 photos
数组中,该数组是在传递给 listObjects
的回调中创建的。
我在 loadPhotos
thunk 操作中做的最后一件事是调用外部 Promise,然后将结果分派给 savePhotos
操作,最终将有效负载对象分派到我的 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
将其转换为数组,但没有成功。
再深入一点,我试图从我的 reducer 中的操作负载中找到 photos
数组的长度,但仍然收到 0
作为输出。我也在 savePhotos
操作中尝试了这个,发现了相同的结果。
因此,我相信我可能没有正确编写我的 Promise。有人可以帮我指明正确的方向吗?
您可以尝试将 listObjects
和 getSingleObject
重写为 return Promise 并利用 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)))
)
}
}
我正在构建一个 React/Redux 项目,该项目对 S3 存储桶进行几次调用以抓取图像,然后将它们渲染到应用程序中。
我发现由于某些原因,我无法遍历我在状态树中设置的数组,因为这些调用将它们呈现到应用程序上。我相信我可能正在处理一个 类数组对象 或我创建的 Promises 中的某些东西导致了这种突变的发生。
首先,我将这个文件放在名为 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
将 return 特定 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 动作。它 return 是一个函数,它首先调用 utils 文件中的 listObjects
函数,向它传递一个回调,创建一个名为 photos
的数组。
然后,它创建一个新的 Promise,循环遍历 return 由 listObjects
效用函数编辑的结果数组。在此结果数组的每次迭代中,我实例化另一个调用 getSingleObject
实用程序的新 Promise。
在那次迭代中,我将 getSingleObject
的结果推送到 photos
数组中,该数组是在传递给 listObjects
的回调中创建的。
我在 loadPhotos
thunk 操作中做的最后一件事是调用外部 Promise,然后将结果分派给 savePhotos
操作,最终将有效负载对象分派到我的 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
将其转换为数组,但没有成功。
再深入一点,我试图从我的 reducer 中的操作负载中找到 photos
数组的长度,但仍然收到 0
作为输出。我也在 savePhotos
操作中尝试了这个,发现了相同的结果。
因此,我相信我可能没有正确编写我的 Promise。有人可以帮我指明正确的方向吗?
您可以尝试将 listObjects
和 getSingleObject
重写为 return Promise 并利用 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)))
)
}
}