无法从操作创建者中分派异步操作
Trouble dispatching async action from within actions creator
我试图在图像作为表单的一部分上传时启动加载微调器,并在对图像的引用保存在 Firebase 中时停止它。
我的 Actions.js 文件中的此函数 returns 来自给定表单字段的输入:
export const formUpdate = ({ prop, value }) => {
alert('update')
return {
type: FORM_UPDATE,
payload: { prop, value }
};
};
使用 Connect,我使用 formUpdate
为我的 Form.js 组件中的不同表单字段存储值 - 这工作正常。
我在 Actions.js 中使用一个单独的函数来处理图像上传,上传后,我调用这个函数在 Firebase 中保存一个引用:
export const saveImageReference = (downloadUrl, sessionId) => {
const { currentUser } = firebase.auth();
firebase
.database()
.ref(`users/${currentUser.uid}/images`)
.push({
imageId: sessionId,
imageUrl: downloadUrl
})
.then(formUpdate({ prop: 'loading', value: false }));
};
我正在尝试让我的表单在上传过程中显示加载微调器。为此,我在 saveImageReference
末尾使用 formUpdate
来发送 loading
道具。但是,这是行不通的。
formUpdate
作为 .then()
块的一部分执行 - 我看到了确认这一点的警报 - 但没有数据进入表单组件。
我也试过使用不同的 prop(例如 'name')来查看它是否更新了表单字段,但没有任何反应。
我 redux-thunk
工作正常 - 我使用类似的方法在我的登录表单中显示一个旋转器 - 但这个动作似乎不想玩球。
如果有帮助,这里是来自我的表单组件的mapStateToProps
:
const { name, location, loading } = state.testForm;
return {
loading,
name,
activity
};
};
export default connect(
mapStateToProps,
{ formUpdate, uploadImage }
)(Form);
更新
这是基于 azundo 的回答的 uploadImage 代码。这不执行:
export const uploadImage = (
uri,
mime = 'application/octet-stream'
) => dispatch => {
const { Blob } = RNFetchBlob.polyfill;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
const { currentUser } = firebase.auth();
console.log('Starting upload action...');
return new Promise((resolve, reject) => {
console.log('in promise 1');
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
const sessionId = new Date().getTime();
// create a reference in firebase storage for the file
let uploadBlob = null;
const imageRef = firebase
.storage()
.ref(`user/${currentUser.uid}/images`)
.child(`image_${sessionId}`);
// encode data with base64 before upload
RNFetchBlob.fs
.readFile(uploadUri, 'base64')
.then(data => {
console.log('Encoding image...');
return RNFetchBlob.polyfill.Blob.build(data, {
type: `${mime};BASE64`
});
})
// put blob into storage reference
.then(blob => {
uploadBlob = blob;
console.log('uploading...');
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
console.log('Getting download URL...');
uploadBlob.close();
return imageRef.getDownloadURL();
})
.then(url => {
console.log('Saving reference...');
// setLoading();
resolve(url);
saveImageReference(url, sessionId);
})
.then(() => {
dispatch(formUpdate({ prop: 'loading', value: false }));
})
.catch(error => {
reject(error);
});
});
};
根据您的描述,saveImageReference
中的 formUpdate
调用实际上并没有调度您的操作,它只是调用了 formUpdate
函数,它只是 returns 普通动作对象。您需要找到一些方法来实际调度该操作。
假设 uploadImage
是一个 redux-thunk
动作,我建议将动作调度的知识保留在 saveImageReference
函数之外,而是从 uploadImage
调度:
export const saveImageReference = (downloadUrl, sessionId) => {
const { currentUser } = firebase.auth();
// note that we are now returning the firebase promise here
return firebase
.database()
.ref(`users/${currentUser.uid}/images`)
.push({
imageId: sessionId,
imageUrl: downloadUrl
});
};
const uploadImage = (arg1, arg2) => dispatch => {
// other upload code here prior to calling the firebase function...
saveImageReference(downloadUrl, sessionId).then(() => {
dispatch(formUpdate({prop: 'loading', value: false}));
});
})
如果您在等待异步操作时尝试呈现加载器。你可以使用悬念。
这将是一个更好的选择。
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<React.Suspense fallback={<Spinner />}>
<div>
<OtherComponent />
</div>
</React.Suspense>
);
}
经过大量调查,我解决了这个问题。我将 saveImageReference()
从 Actions.js 移动到我的组件:
addImage = () => {
ImagePicker.showImagePicker(response => {
if (!response.didCancel) {
// shows modal with a form for user to select an image and add metadata
this.setState({ showModal: true });
// set the loading spinner in the form to show image is uploading
this.props.formUpdate({ prop: 'loading', value: true });
// takes the selected image and invokes uploadImage
uploadImage(response.uri).then(url => {
// once image is uploaded, generate sessionId in the component, and invoke saveImageReference
const sessionId = new Date().getTime();
this.props.saveImageReference(url, sessionId);
});
}
});
};
uploadImage()
操作创建者使用成功上传图片的 URL 进行解析,saveImageReference()
使用该图片创建引用。
保存该引用后,saveImageReference()
会调度一个专门的操作来将加载设置为 false。这是Actions.js的内容:
export const uploadImage = (uri, mime = 'application/octet-stream') => {
const { Blob } = RNFetchBlob.polyfill;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
const { currentUser } = firebase.auth();
console.log('Starting upload action...');
return new Promise((resolve, reject) => {
console.log('in promise');
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
const sessionId = new Date().getTime();
// create a reference in firebase storage for the file
let uploadBlob = null;
const imageRef = firebase
.storage()
.ref(`user/${currentUser.uid}/images`)
.child(`image_${sessionId}`);
// encode data with base64 before upload
RNFetchBlob.fs
.readFile(uploadUri, 'base64')
.then(data => {
console.log('Encoding image...');
return RNFetchBlob.polyfill.Blob.build(data, {
type: `${mime};BASE64`
});
})
// put blob into storage reference
.then(blob => {
uploadBlob = blob;
console.log('uploading...');
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
console.log('Getting download URL...');
uploadBlob.close();
return imageRef.getDownloadURL();
})
.then(url => {
resolve(url, sessionId);
})
.catch(error => {
reject(error);
});
});
};
export const saveImageReference = (downloadUrl, sessionId) => {
const { currentUser } = firebase.auth();
console.log('Saving reference!');
return dispatch => {
firebase
.database()
.ref(`users/${currentUser.uid}/images`)
.push({
imageId: sessionId,
imageUrl: downloadUrl
})
.then(ref => {
console.log(ref.key);
dispatch(imageUploadComplete());
});
};
};
const imageUploadComplete = () => {
return dispatch => {
return dispatch({
type: IMAGE_CREATE,
payload: false
});
};
};
无论我尝试什么,我都无法从 saveImageReference()
中发送另一个操作 - 引入 return dispatch
会冻结流程,没有它我会得到 dispatch is not defined
。
在组件级别调用它,使用 this.props.saveImageReference()
解决了问题。
我试图在图像作为表单的一部分上传时启动加载微调器,并在对图像的引用保存在 Firebase 中时停止它。
我的 Actions.js 文件中的此函数 returns 来自给定表单字段的输入:
export const formUpdate = ({ prop, value }) => {
alert('update')
return {
type: FORM_UPDATE,
payload: { prop, value }
};
};
使用 Connect,我使用 formUpdate
为我的 Form.js 组件中的不同表单字段存储值 - 这工作正常。
我在 Actions.js 中使用一个单独的函数来处理图像上传,上传后,我调用这个函数在 Firebase 中保存一个引用:
export const saveImageReference = (downloadUrl, sessionId) => {
const { currentUser } = firebase.auth();
firebase
.database()
.ref(`users/${currentUser.uid}/images`)
.push({
imageId: sessionId,
imageUrl: downloadUrl
})
.then(formUpdate({ prop: 'loading', value: false }));
};
我正在尝试让我的表单在上传过程中显示加载微调器。为此,我在 saveImageReference
末尾使用 formUpdate
来发送 loading
道具。但是,这是行不通的。
formUpdate
作为 .then()
块的一部分执行 - 我看到了确认这一点的警报 - 但没有数据进入表单组件。
我也试过使用不同的 prop(例如 'name')来查看它是否更新了表单字段,但没有任何反应。
我 redux-thunk
工作正常 - 我使用类似的方法在我的登录表单中显示一个旋转器 - 但这个动作似乎不想玩球。
如果有帮助,这里是来自我的表单组件的mapStateToProps
:
const { name, location, loading } = state.testForm;
return {
loading,
name,
activity
};
};
export default connect(
mapStateToProps,
{ formUpdate, uploadImage }
)(Form);
更新
这是基于 azundo 的回答的 uploadImage 代码。这不执行:
export const uploadImage = (
uri,
mime = 'application/octet-stream'
) => dispatch => {
const { Blob } = RNFetchBlob.polyfill;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
const { currentUser } = firebase.auth();
console.log('Starting upload action...');
return new Promise((resolve, reject) => {
console.log('in promise 1');
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
const sessionId = new Date().getTime();
// create a reference in firebase storage for the file
let uploadBlob = null;
const imageRef = firebase
.storage()
.ref(`user/${currentUser.uid}/images`)
.child(`image_${sessionId}`);
// encode data with base64 before upload
RNFetchBlob.fs
.readFile(uploadUri, 'base64')
.then(data => {
console.log('Encoding image...');
return RNFetchBlob.polyfill.Blob.build(data, {
type: `${mime};BASE64`
});
})
// put blob into storage reference
.then(blob => {
uploadBlob = blob;
console.log('uploading...');
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
console.log('Getting download URL...');
uploadBlob.close();
return imageRef.getDownloadURL();
})
.then(url => {
console.log('Saving reference...');
// setLoading();
resolve(url);
saveImageReference(url, sessionId);
})
.then(() => {
dispatch(formUpdate({ prop: 'loading', value: false }));
})
.catch(error => {
reject(error);
});
});
};
根据您的描述,saveImageReference
中的 formUpdate
调用实际上并没有调度您的操作,它只是调用了 formUpdate
函数,它只是 returns 普通动作对象。您需要找到一些方法来实际调度该操作。
假设 uploadImage
是一个 redux-thunk
动作,我建议将动作调度的知识保留在 saveImageReference
函数之外,而是从 uploadImage
调度:
export const saveImageReference = (downloadUrl, sessionId) => {
const { currentUser } = firebase.auth();
// note that we are now returning the firebase promise here
return firebase
.database()
.ref(`users/${currentUser.uid}/images`)
.push({
imageId: sessionId,
imageUrl: downloadUrl
});
};
const uploadImage = (arg1, arg2) => dispatch => {
// other upload code here prior to calling the firebase function...
saveImageReference(downloadUrl, sessionId).then(() => {
dispatch(formUpdate({prop: 'loading', value: false}));
});
})
如果您在等待异步操作时尝试呈现加载器。你可以使用悬念。
这将是一个更好的选择。
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<React.Suspense fallback={<Spinner />}>
<div>
<OtherComponent />
</div>
</React.Suspense>
);
}
经过大量调查,我解决了这个问题。我将 saveImageReference()
从 Actions.js 移动到我的组件:
addImage = () => {
ImagePicker.showImagePicker(response => {
if (!response.didCancel) {
// shows modal with a form for user to select an image and add metadata
this.setState({ showModal: true });
// set the loading spinner in the form to show image is uploading
this.props.formUpdate({ prop: 'loading', value: true });
// takes the selected image and invokes uploadImage
uploadImage(response.uri).then(url => {
// once image is uploaded, generate sessionId in the component, and invoke saveImageReference
const sessionId = new Date().getTime();
this.props.saveImageReference(url, sessionId);
});
}
});
};
uploadImage()
操作创建者使用成功上传图片的 URL 进行解析,saveImageReference()
使用该图片创建引用。
保存该引用后,saveImageReference()
会调度一个专门的操作来将加载设置为 false。这是Actions.js的内容:
export const uploadImage = (uri, mime = 'application/octet-stream') => {
const { Blob } = RNFetchBlob.polyfill;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
const { currentUser } = firebase.auth();
console.log('Starting upload action...');
return new Promise((resolve, reject) => {
console.log('in promise');
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
const sessionId = new Date().getTime();
// create a reference in firebase storage for the file
let uploadBlob = null;
const imageRef = firebase
.storage()
.ref(`user/${currentUser.uid}/images`)
.child(`image_${sessionId}`);
// encode data with base64 before upload
RNFetchBlob.fs
.readFile(uploadUri, 'base64')
.then(data => {
console.log('Encoding image...');
return RNFetchBlob.polyfill.Blob.build(data, {
type: `${mime};BASE64`
});
})
// put blob into storage reference
.then(blob => {
uploadBlob = blob;
console.log('uploading...');
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
console.log('Getting download URL...');
uploadBlob.close();
return imageRef.getDownloadURL();
})
.then(url => {
resolve(url, sessionId);
})
.catch(error => {
reject(error);
});
});
};
export const saveImageReference = (downloadUrl, sessionId) => {
const { currentUser } = firebase.auth();
console.log('Saving reference!');
return dispatch => {
firebase
.database()
.ref(`users/${currentUser.uid}/images`)
.push({
imageId: sessionId,
imageUrl: downloadUrl
})
.then(ref => {
console.log(ref.key);
dispatch(imageUploadComplete());
});
};
};
const imageUploadComplete = () => {
return dispatch => {
return dispatch({
type: IMAGE_CREATE,
payload: false
});
};
};
无论我尝试什么,我都无法从 saveImageReference()
中发送另一个操作 - 引入 return dispatch
会冻结流程,没有它我会得到 dispatch is not defined
。
在组件级别调用它,使用 this.props.saveImageReference()
解决了问题。