0

So I may overthinking this issue, but I can't seem to make my component work asynchronously.

Essentially I am using thunk to dispatch an action that will retrieve a JSON from a local server and once it receives, pass it down to the reducer and update the state, thus then rendering the component.

Instead, the component receives the app's intitial state as {} empty object, thus giving an error when I am using map to render the component.

Here's what I have:

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import thunk from 'redux-thunk';
import promise from 'redux-promise';

import App from './components/app';
import VotesContainer from './containers/votes-container';
import NewPoll from './containers/newpoll';
import VoteTemplate from './components/vote-template';
import Login from './components/auth/login';
import Signup from './components/auth/signup';
import reducers from './reducers/';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(reducers, composeEnhancers(applyMiddleware(thunk)))

ReactDOM.render(
  <Provider store={store}>
    <Router history={browserHistory}>
      <Route path="/" component={App}>
        <IndexRoute component={VoteTemplate} />
        <Route path="allposts" component={VotesContainer} />
        <Route path="login" component={Login} />
        <Route path="signup" component={Signup} />
        <Route path="newpoll" component={NewPoll} />
      </Route>
    </Router>
  </Provider>
  , document.querySelector('#project'));

actions/index.js

import axios from 'axios';
const ROOT_URL = 'http://localhost:1234';
import { browserHistory } from 'react-router';
import { FETCH_VOTES, CAST_VOTE } from './types';

export function fetchVotes(){
  return function(dispatch){
    axios.get(`${ROOT_URL}/newpost`)
      .then(response => {
        console.log('Dispatch!!')
        dispatch({
          type: FETCH_VOTES,
          payload: response.data
        })
      })
  }
}

container

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

class VotesContainer extends Component {
  componentDidMount(){
    this.props.fetchVotes()
  }
  renderCards(){
    return(
      <div className="col s12 m6 l4">
        <div className="card blue-grey darken-1">
          <div className="card-content white-text">
            <span className="card-title">Vote App Title #1</span>
            <p className="description">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
          </div>
          <div className="card-action">
            <a href="#">Results</a>
            <a href="#">Date Created</a>
          </div>
        </div>
      </div>
    )
  }
  render(){
    console.log(this.props.voteData)
    return(
      <main className="votes-container container">
        <div className="row">
          {this.props.voteData.map(this.props.voteData.vote)} <<======= gives error since it is null first before dispatch
        </div>
      </main>
    )
  }
}

function mapStateToProps(state){
  return {voteData: state.voteData};
}

export default connect(mapStateToProps, actions)(VotesContainer)
8
  • You can't use map on an object, are you trying to use it for object keys? Commented Dec 8, 2016 at 5:09
  • sorry it's a typo, it should be this.props.voteData.vote Commented Dec 8, 2016 at 5:12
  • 1
    You can guard against the array. Doing (this.props.voteData.vote || []).map(this.renderCards) will do what you want. You will start from the initial state which is an empty object then when you dispatch it will be populated unless you want to start with another initial state from server? Commented Dec 8, 2016 at 5:15
  • Awesome, that look like it did the job! However, I am still curious why my setup with redux-thunk isn't working correctly Commented Dec 8, 2016 at 5:19
  • 1
    Here is more info: github.com/reactjs/redux/blob/master/docs/advanced/…. Basically, you need to dispatch an action before calling the API that will modify state to be FETHCING and then once you have a response you change that to be DONE. The attached link explains that well. Commented Dec 8, 2016 at 5:52

1 Answer 1

1

You should set some initial state of the store with what you expect in the component.
If you're not doing that then you shouldn't expect all data to be there in first render. Data will come only after fetching, till then you might as well add some safe checks in the component.

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

2 Comments

Ahh I see. I would've thought that when componentDidMount is called, and it is fetching asynchronously, it would not render the component until the data is fetched. Yes, by simply adding safe checks, it works perfectly
@Alejandro: No, while it's waiting for data, render will be called as waiting for data will be an async process. Once data is received -> state is updated -> render is called again

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.