无法从动作创建者中调度异步动作

时间:2019-08-24 08:43:00

标签: react-native redux react-redux redux-thunk

当图像作为表单的一部分上载时,我试图启动加载微调器,并在Firebase中保存对图像的引用时停止它。

我的Actions.js文件中的此函数从给定的表单字段返回输入:

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

我正在尝试让我的表单在上传过程中显示加载微调框。为此,我在formUpdate的末尾使用saveImageReference来分发loading道具。但是,这不起作用。

formUpdate作为.then()块的一部分执行-我看到警报来确认这一点-但没有数据传递给Form组件。

我还尝试使用其他道具(例如“名称”)来查看它是否更新了表单字段,但是什么也没有发生。

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

3 个答案:

答案 0 :(得分:1)

根据您的描述,formUpdate中的saveImageReference调用实际上并没有调度您的操作,它只是调用简单的formUpdate函数,该函数仅返回简单的操作对象。您需要找到某种方法来实际执行该操作。

假设uploadImageredux-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}));
  });

})

答案 1 :(得分:0)

如果您尝试渲染加载器,则在等待异步操作时。您可以使用悬念。

这将是一个更好的选择。

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <React.Suspense fallback={<Spinner />}>
      <div>
        <OtherComponent />
      </div>
    </React.Suspense>
  );
}

答案 2 :(得分:0)

经过大量调查,我解决了这个问题。我将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()使用该URL创建引用。

保存引用后,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()在组件级别调用此功能即可解决此问题。