I just started to work on React Js and Redux-Thunk. Currently, I am trying to fetch data from a url using redux-thunk. I got data successfully but the issue is that it renders undefined
data twice, then it gives me the desired data in props. Here is my code.
In Actions index.js
function getData() {
return {
type: 'FETCH'
}
}
function getSuccess(data) {
return {
type: 'FETCH_SUCCESS',
payload: data
}
}
function getFailed(err) {
return {
type: 'FAILED',
payload: err
}
}
export function fetchData() {
const thunk = async function thunk(dispatch) {
try {
dispatch(getData());
const body = await fetch("http://jsonplaceholder.typicode.com/users")
const res = await body.json();
console.log("Thunk", res);
dispatch(getSuccess(res));
}
catch(err) {
dispatch(getFailed(err));
}
}
return thunk;
}
In Reducers fetch.js
const initialState = {
state: []
}
export default function(state=initialState , actions) {
switch(actions.type) {
case 'FETCH':
return {
...state
}
case 'FETCH_SUCCESS':
return {
...state,
data: actions.payload
}
case 'FAILED':
return {
...state
}
default:
return state;
}
}
Reducers Index.js
import fetch from './fetch';
import { combineReducers } from 'redux';
const rootReducer = combineReducers ({
fetch
});
export default rootReducer;
App.js
import React, { Component } from 'react';
import './App.css';
import Main from './component/Main';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
console.log("STore", store.getState());
class App extends Component {
render() {
return (
<Provider store={store}>
<Main/>
</Provider>
);
}
}
export default App;
Main.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../actions';
class Main extends Component {
componentWillMount() {
const { fetchData } = this.props
fetchData();
}
render() {
let mydata = this.props.data.data;
console.log("Data .....<>", mydata);
return(
<div>
Main
</div>
);
}
}
const mapStateToProps =(state)=> {
return {
data: state.fetch
}
}
export default connect(mapStateToProps, {fetchData: actions.fetchData})(Main);
Please let me know what i am doing wrong. Any help will be appreciated. Thanks
This behavior is correct. You're doing everything in the normal way, except calling async operations in componentWillMount
method instead of componentDidMount
. Read more here about it.
You need to know, that when you are using componentDidMount
- you are handle this in a safe way due to commit
phase in component lifecycle and it means that your request will be guaranteed trigger once instead of possible several times, which can be in render
phase.
See here the visual diagram to understand this more.
Regarding several renderings - it can be explained easily. First time, when your component is mounting you are calling asynchronous operation and it needs more time to load data than for component mounting. Thats why you are accessing data
property of your initialState (which is empty array), and getting undefined
.
When you response is ready and actions is being dispatched, redux trigger re-render of connected components with new state.
If you want to make your async-await
syntax works you should make lifecycle with async
keyword as well and await
your request inside.
NOTE : It's safe, but in several cases it might cause unexpected bugs, so keep in mind. Nevertheless, I don't recommend to use it in a such way. Read about this topic in the another thread at SO.
I advice you to create some isLoading
boolean status in your reducer and keep track whether data is loaded or not.
Good luck! Hope it will helps!
There is nothing wrong with your code, but there is one unnecessary action. Why do you see two times undefined
and then you see your data?
First one is coming from the initial render. You are making an async dispatching in your componentWillMount
so render
does not wait for it, then try to log your data. At that time it is undefined
.
Your fetchData
dispatches two actions: getData
and getSuccess
. You see second undefined
because of getData
action since it returns current state which props.data.data
undefined at that time again.
You see the data since getSuccess
updates your state now.
If you want to test this comment out getData
in your fetchData
function, just use getSuccess
and change your console.log
in the render like that:
mydata && console.log("Data .....<>", mydata);
I think getData
action is unnecessary here, it does not do anything beside returning the current state. Also, try to use componentDidMount
instead of componentWillMount
since it will be deprecated in version 17.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.