问题
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