简体   繁体   中英

ReactJs Redux Data Fetch

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);

Output Screen Shot

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?

  1. 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 .

  2. 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.

  3. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM