FileReader - how to update local state after store is updated?

Viewed 291

I'm playing around with a food recognition api.

I have a component with a local state called ingredients.

In the component, I have an input tag that accepts a file image upload and calls cameraHandler method onChange. The method uses FileReader to convert the image into Base64

Once the FileReader is finished encoding the image, the method calls a redux action fetchIngredientsFromImage to post the base64 image into a route to trigger to trigger an API call (to analyze the ingredients in the image).

The response is sent back to the front end, and used to update store.

So basically, the API call is successful, I get the data I need, and store is updated successfully. Great.

But what I also need to do, is update my local ingredients state. But I don't know how to wait for store to be updated before calling setState.

I've tried componentDidUpdate with if(this.props !== prevProps) methodToUpdateLocalState(), but this doesn't work because for some reason the component won't re-render after store is updated.. Turns out that everything inside componentDidUpdate runs first, and store is updated afterwards. I feel like also isn't necessary (probably).

I also tried .then the awaited readers inside cameraHandler, but .then is undefined.

I'd appreciate any input I could get. Really at a loss here, because I have the data, and I just need to somehow grab it so I can setState.

Component

class RecipesSearch extends Component {
  state = {
    ingredients: [], //need to update this after store is updated, but how?
  };

  cameraHandler = async (event) => {
    const { fetchIngredientsFromImage } = this.props;
    const file = event.target.files[0];
    const reader = new FileReader();
    await reader.readAsDataURL(file);
    reader.onloadend = async () => {
      const imgBase = reader.result.replace(/^data:image\/(.*);base64,/, '');
      await fetchIngredientsFromImage(imgBase); //.then here is undefined
    };
  };

render(){
  <input
    className="form-check-input"
    type="file"
    name="camera"
    accept="image/*"
    onChange={this.cameraHandler}
  />
}

Actions

const fetchIngredientsFromImage = (imgBase) => async (dispatch) => {
  const { data } = await axios.post(`/api/camera/`, { imgBase });
  return dispatch(setIngredientsFromCamera(data)); //successfully updates store
};
1 Answers

as a workaround I made an axios.post call inside cameraHandler. Not proud of it, because I'd like to utilize store and keep it consistent with my other methods, but for the time being it'll do I guess.

cameraHandler = async (event) => {
    // const { loadIngredientsFromImage } = this.props;
    const file = event.target.files[0];
    const reader = new FileReader();
    await reader.readAsDataURL(file);
    reader.onloadend = async () => {
      const imgBase = reader.result.replace(/^data:image\/(.*);base64,/, '');
      await axios
        .post(`/api/camera/`, { imgBase })
        .then((response) => this.setState({ ingredients: response.data }));
    };
  };
Related