Nick G.
Nick G.

Reputation: 77

Reactjs - Uncaught TypeError: Cannot read property 'then' of undefined

I have a problem here: I am always getting this error when I am trying to use this code in componentDidMount: Uncaught TypeError: Cannot read property 'then' of undefined

This is the code of the Component where the error occurs: components/Portfolio.jsx

import React, { Component } from 'react'
import Header from './Header'
import { connect } from 'react-redux';
import { auth } from '../actions';
import Popup from 'reactjs-popup';
import { portfolio } from '../actions'
import thunk from 'redux-thunk'

class Portfolio extends Component {

  state = {
    coin: undefined,
    quantity: undefined,
    buy_price: undefined,
    coindata: undefined,
    isLoaded: undefined,
  } 

    componentDidMount(){
        // this.props.fetchPortfolio();
        //   this.fetchCoins();
      this.props.fetchPortfolio().then(() => {
      this.fetchCoins();
      });
    }

  componentDidUpdate(prevProps) {
    if(this.props.auth.user !== prevProps.auth.user) {
      this.fetchCoins();
    }
  }


  submitInvestment = (e) => {
    e.preventDefault();
    this.props.addInvestment(this.state.coin, this.state.quantity,             this.state.buy_price);
    this.props.fetchPortfolio();
    this.fetchCoins();
  }


  fetchCoins(){

    let coins = this.props.portfolio.map(coin => coin.coin).join(",");
    console.log(coins);
    //console.log(coins)
    fetch("https://min-api.cryptocompare.com/data/pricemultifull?    fsyms="+coins+"&tsyms=USD")
        .then(res => res.json())
        .then(
          (result) => {
            console.log(result)
            this.setState({
              isLoaded: true,
              coindata: result,
            })
          },
          (error) => {
            this.setState({
              isLoaded: true,
              error
            });
          }
        )

  }



  render() {
    ...
   }
  }
}

const mapStateToProps = state => {
    let errors = [];
    if (state.portfolio.errors) {
        errors = Object.keys(state.portfolio.errors).map(field => {
            return {field, message: state.portfolio.errors[field]};
        });
    }
    return {
      user: state.auth.user,
      auth: state.auth,
      portfolio: state.portfolio.portfolio,
      portfolioLoaded: state.portfolio.portfolioLoaded,
      coin_symbol: state.coin,
      errors
      // errors
    }
}

const mapDispatchToProps = dispatch => {
    return {
        addInvestment: (coin, quantity, buy_price) => {
          return dispatch(portfolio.addInvestment(coin, quantity, buy_price));
        },
        deleteInvestment: (coin) => {
          dispatch(portfolio.deleteInvestment(coin));
        },
        fetchPortfolio: () => {
          dispatch(portfolio.fetchPortfolio());
        }
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Portfolio); //

This is the fetchPortfolio function in actions/portfolio.js:

export const fetchPortfolio = () => {
  return (dispatch, getState) => {
    let headers = {'Content-Type': 'application/json'}
    let {token} = getState().auth

    if (token) {
      headers['Authorization'] = `Token ${token}`
    }

    return fetch('/api/coins/', {headers })
      .then(res => {
        if (res.status < 500) {
          return res.json().then(data => {
            return {status: res.status, data}
          })
        } else {
          console.log('Server Error!')
          throw res
        }
      })
      .then(res => {
        if (res.status === 200) {
          dispatch({type: 'FETCH_PORTFOLIO', portfolio: res.data})
        } else if (res.status === 401 || res.status === 403) {
          dispatch({type: 'AUTHENTICATION_ERROR', data: res.data})
          throw res.data
        }
      })
  }
}

And this is the App.js code:

import React, { Component } from 'react'

import {Switch, Route, BrowserRouter, Redirect, Link} from 'react-router-dom'
import { Provider, connect } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

import {auth} from './actions'
import rootReducer from './reducers'

import News from './components/News'
import NotFound from './components/NotFound'
import Login from './components/Login'
import Register from './components/Register'
import Market from './components/Market'
import Portfolio from './components/Portfolio'

let store = createStore(rootReducer, applyMiddleware(thunk))

class RootContainerComponent extends Component {
  componentDidMount () {
    this.props.loadUser()
  }

  render () {
    if (this.props.auth.isLoading) {
      return <p>Loading..</p>
    } else {
      return (
        <BrowserRouter>
          <Switch>
            <Route exact path='/' component={News} />
            <Route exact path='/login' component={Login} />
            <Route exact path='/register' component={Register} />
            <Route exact path='/markets' component={Market} />
            <Route exact path='/portfolio' component={Portfolio} />
            <Route component={NotFound} />
          </Switch>
        </BrowserRouter>
      )
    }
  }
}

const mapStateToProps = state => {
  return {
    auth: state.auth
  }
}

const mapDispatchToProps = dispatch => {
  return {
    loadUser: () => {
      return dispatch(auth.loadUser())
    }
  }    
}

let RootContainer = connect(mapStateToProps, mapDispatchToProps)        (RootContainerComponent)

export default class App extends Component {
  render () {
    return (
      <Provider store={store}>
        <RootContainer />
      </Provider>
    )
  }
}

I hope you can help me with the problem!

Upvotes: 0

Views: 2551

Answers (1)

mcssym
mcssym

Reputation: 944

In mapDispatchToProps your fetchPortfolio doesn’t return a promise or anything else. You must return a promise inside it but dispatch doesn’t return something.

You have to wait your props being updated by mapStateToProps

You have portfolioLoaded which is mapped to your props. Just check his value in your componentWillReceiveProps and if it’s true now you can call this.fetchCoins

Upvotes: 3

Related Questions