react redux with asynchronous fetch

别来无恙 提交于 2021-02-08 10:18:27

问题


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.


回答1:


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!



来源:https://stackoverflow.com/questions/52821157/react-redux-with-asynchronous-fetch

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!