0
votes

I am porting over a React application to use Redux. I can see that the action is firing on the click event, but when I add console.log to the action in the reducer, I'm seeing an undefined value.

I am using Redux thunk since I'm fetching JSON data with the fetch API.

the generateQuote action should return a new quote.

Is there anything that I'm doing that is clearly wrong?

reducers/quotes.js


const defaultState = 
{ quote: 'Hello World',
  name: 'Me',
  email: '[email protected]',
  date: Date(Date.now()),
  generatedQuotes: []

 }

const quotes = (state = defaultState, action) => {
  switch(action.type) {
    case GENERATE_QUOTE : 
      console.log(action.payload)
      return { ...state, quotes: action.payload, date: action.date } 
    case DELETE_QUOTE : 
        return {
          generatedQuotes: [...state.generatedQuotes.filter(quote => quote !== action.payload)]}
    case SHARE_QUOTE : 
          return state;
    default: 
      return state;
  }
}

export default quotes;

actions/index.js

export const generateQuote = () => dispatch => {
  const randomNum = (Math.floor(Math.random() * (500 - 1)) + 1);
  fetch(`https://jsonplaceholder.typicode.com/comments?id=${randomNum.toString()}`)
  .then(res => res.json())
  .then(data => {
    return 
    dispatch( {
      type: GENERATE_QUOTE, 
      payload: data[0],
      date: Date(Date.now())
    })
  })
}

Quote.js (component)

/* eslint-disable react/require-default-props */
/* eslint-disable react/button-has-type */
/* eslint-disable react/jsx-filename-extension */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';

const Quote = ({quote, author, email, date, generateQuote, shareQuote}) => {

  return (
    <div className="box">
      <p>
        <strong>Quote:</strong>
        {' '}
        {quote}
      </p>
      <p>
        <strong>Author:</strong>
        {' '}
        {author}
      </p>
      <p>
        <strong>Email:</strong>
        {' '}
        {email}
      </p>
      <p>
        <strong>Date:</strong>
        {' '}
        {date}
      </p>
      <br />
      <div className="buttons">
        <button className="button is-primary is-small" onClick={generateQuote}>New Quote</button>
        <button className="button is-success is-small" onClick={shareQuote}>Tweet Quote</button>
      </div>

    </div>
  );
};

Quote.propTypes = {
  quote: PropTypes.string,
  author: PropTypes.string,
  email: PropTypes.string,
  date: PropTypes.string,
  generateQuote: PropTypes.func,
  shareQuote: PropTypes.func,
};

const mapStateToProps = state => {
  const { quote, author, email, date} = state;
};

const mapDispatchToProps = dispatch => {
  return {
    generateQuote: () => dispatch({type: 'GENERATE_QUOTE'}),
    shareQuote: () => dispatch({type: 'SHARE_QUOTE'})
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(Quote);
2
Have you console logged data[0] in your generateQuote action?Drazen Bjelovuk
I did try console logging data[0], but it didn't return anything. I wonder if that means that the action isn't actually being dispatched? I'm just learning Redux right now so I'm still trying to get the hang of how everything works together.Joseph LoPresti
It likely means your api call isn't returning what you expect.Drazen Bjelovuk
Which is strange. I have the exact same API call using local state. if you look at generateQuote here I do pretty much the same thing. github.com/fendermaniac/random-quote-machine-react/blob/master/…Joseph LoPresti

2 Answers

0
votes

Looks like you need to dispatch your quote action in your component. You're dispatching to your reducer twice. Once in the component and another in the actions file. Is the function in the actions file actually getting fired?

Examples below.

https://jsonplaceholder.typicode.com/comments?id=101 responds with an array,

[
   {
      postId: 21,
      id: 101,
      ....
   }
] 

Cool. static number works. the logic in your fetch is correct.

What I'm curious about is if your actual fetch is being called/fired. In your component you're calling mapStateToProps' generateQuote's value => dispatch({type: 'GENERATE_QUOTE'}), like so...

const mapDispatchToProps = dispatch => {
  return {
    generateQuote: () => dispatch({type: 'GENERATE_QUOTE'}),
    shareQuote: () => dispatch({type: 'SHARE_QUOTE'})
  };
}

Then, you're dispatching it again within the generateQuote in your actions/index.js

I may be missing something, but you might be skipping your action all together.

// ...are you sure this is firing? 
// console.log's never killed a cat. 
dispatch({
  type: GENERATE_QUOTE, 
  payload: data[0],
  date: Date(Date.now())
})

I would put your action inside your mapStateToProps() function, like so...

import QuoteActions from '../actions"

// ... blah blah blah

const mapDispatchToProps = (dispatch) => {
  return {
    fetchProfile: (credentials) => {
      dispatch(QuoteActions.generateQuote()) // << HERE
    }
  }
}

Hope this helps.

0
votes

I think that return statement is missing:

export const generateQuote = () => dispatch => {
  const randomNum = (Math.floor(Math.random() * (500 - 1)) + 1);
  return fetch(`https://jsonplaceholder.typicode.com/comments?id=${randomNum.toString()}`)
  .then(res => res.json())
  .then(data => 
    dispatch({
      type: GENERATE_QUOTE, 
      payload: data[0],
      date: Date(Date.now())
  }) 
 ).catch(error => console.log(error))
}

If the error persists, try the request using a static number just for test:

https://jsonplaceholder.typicode.com/comments?id=1