0

I am using react redux, and trying to call rest-api, and return the data. The fetch is asynchronous, and I need to return value only when the fetch is finished.

I tried the following, but this didn't work properly (there is no 'wait' on the line that is with asterisks) - What shall I do?

Code:

-----

var _getData = function()  
{
return new Promise((resolve, reject) => {
    let options = {
        method: 'GET',
    }
    let _url = "my web return json";
    fetch(_url, options)
    .then(response => 
        response.json().
            then(data => resolve({
                data: data,
                status: response.status
            }))
            .catch(err => reject(
                {error_data: err}
                )
            )

    )
    .catch(
        err =>
        {
            console.log(err);
            reject(err);
        }
    )
    })
}

// ...
const reducer = (state = initialState, action) => {
if (action.type === 'GET_DATA_FIRST') {
    let x = {}
    setTimeout(function () {
        _getData().then(
            (res) =>
            {
                x = res;
            }
        ) }, 0)
    // ******** NEED TO WAIT UTIL _getData ends and return a value ***** 


    return {
        ...state,
        myData: x
    }
}
return state;
};

Thanks.

6
  • First off, probably don't perform async operators inside a redux reducer. Instead use an async action creator and dispatch the action/payload to the reducer upon resolution inside then()/catch(). There are numerous middleware libraries such as redux-thunk to make this easy. Commented Oct 15, 2018 at 16:42
  • Can you produce me some examples, please. Commented Oct 15, 2018 at 16:43
  • You should really look at the documentation on Async Actions and redux-thunk and restructure your actions/reducers to match the described patterns then if you are still having issues, update your question with the relevant issues. Also there are great examples showing async redux applications. Really you would just be moving you async call to your action creator rather than the reducer. Commented Oct 15, 2018 at 16:44
  • OK. There are many documentaions. One of the documentations is as the code of mine. The code you sent is totaly different. Not ressumble the code of mine. Thank you. I will watch that. Commented Oct 15, 2018 at 18:06
  • Well. Now I got: "Uncaught Error: Actions must be plain objects. Use custom middleware for async action" even I had copied the same objects as described in the above link "Async actions". (even I did a single line: return "123"; - it failed with same error message as described. Commented Oct 15, 2018 at 18:33

1 Answer 1

3

At a very basic level you need the move the asynchrounous operation out of your reducer. Instead you should trigger the fetch() in your action creator and dispatch an action once the fetch() has completed and resolved your JSON response. It would look something like the following. This example uses redux- thunk for async middleware. An additional action has been added to represent when the fetch() call starts and when the data is received and parsed. This can be used to show a loading message or perhaps disable and/or conditionally render specific things.

I've created a working example.

Store:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const middleware = [ thunk ];

const store = createStore(
  rootReducer,
  applyMiddleware(...middleware)
);

export default store;

Reducer:

import { combineReducers } from 'redux';
import { GET_DATA_FIRST, REQUEST_DATA } from '../actions';

const initialState = {
  isFetching: false,
  myData: []
};

const things = (state = initialState, action) => {
  switch (action.type) {
    case REQUEST_DATA:
      return {
        ...state,
        isFetching: true
      };
    case GET_DATA_FIRST:
      return {
        ...state,
        isFetching: false,
        myData: action.myData
      };
    default:
      return state;
  }
};

const rootReducer = combineReducers({
  things // this key can be called anything, 'things' is just an example
});

export default rootReducer;

Action:

export const REQUEST_DATA = 'REQUEST_DATA'; // action to represent waiting for response
export const GET_DATA_FIRST = 'GET_DATA_FIRST'; // action to represent receiving of data

export const requestData = () => ({ type: REQUEST_DATA });

export const getDataFirst = myData => ({ type: GET_DATA_FIRST, myData });

export const fetchData = () => dispatch => {
  dispatch(requestData());
  return getData().then(things => {
    // simulated delay
    setTimeout(() => {
      return dispatch(getDataFirst(things))
    }, 1000);
  });
};

const getData = () => {  
  return fetch('https://jsonplaceholder.typicode.com/todos').then(res => res.json());
}

Component:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchData } from './actions';

class ThingsList extends Component {
  constructor(props) {
    super(props);    
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.props.dispatch(fetchData());
  }

  render() {
    return (
      <div>
        <button type="button" onClick={this.handleClick}>GET DATA</button>
        {this.props.isFetching && <div>Loading...</div>}
        <ul>
          {this.props.myData.map(d => <li key={d.id}>{d.title}</li>)}
        </ul>        
      </div>
    );
  }
}

const mapStateToProps = ({ things: { myData, isFetching } }) => ({
  myData,
  isFetching
});

export default connect(mapStateToProps)(ThingsList);

Notice how the actions such as fetchData() pass dispatch to inner action functions. This is used to dispatch actions/payloads to the reducer. The reducer should just get ready-to-use data to update state with.

Hopefully that helps!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.