0

I have set up an API with Rails, with a http://localhost:3001/api/words endpoint exposing the following data:

[{"id":1,"term":"Reach","definition":"Reach is the number of people who had any content from your Page or about your Page enter their screen.","example":"","author":"Loomly","credit":"https://www.loomly.com/","created_at":"2018-11-02T03:21:20.718Z","updated_at":"2018-11-02T03:21:20.718Z"},{"id":2,"term":"Emoji","definition":"A small digital image or icon used to express an idea, emotion, etc., in electronic communication","example":"","author":"Loomly","credit":"https://www.loomly.com/","created_at":"2018-11-02T03:23:50.595Z","updated_at":"2018-11-02T03:23:50.595Z"}]

I am now trying to simply display this data (ideally as an unordered list) in a React.js frontend application built with Create React App, and here is the content of my App.js file:

import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor () {
    super()
    this.state = {}
    this.getWords = this.getWords.bind(this)
    this.getWord = this.getWord.bind(this)
  }

  componentDidMount () {
    this.getWords()
  }

  fetch (endpoint) {
    return window.fetch(endpoint)
      .then(response => response.json())
      .catch(error => console.log(error))
  }

  getWords () {
    this.fetch('/api/words')
      .then(words => {
        if (words.length) {
          this.setState({words: words})
          this.getWord(words[0].id)
        } else {
          this.setState({words: []})
        }
      })
  }

  getWord (id) {
    this.fetch(`/api/words/${id}`)
      .then(word => this.setState({word: word}))
  }

  render () {
    let {words, word} = this.state
    return (
      <div>
        {Object.keys(words).map((key) => {
          return (
            <div key={word.id}>
              <p>{word.term}</p>;
            </div>
          )
        })}
      </div>
    )
  }
}

export default App;

I believe the problem is located in the following area of the code:

render () {
  let {words, word} = this.state
    return (
      <div>
        {Object.keys(words).map((key) => {
          return (
            <div key={word.id}>
              <p>{word.term}</p>;
            </div>
          )
        })}
      </div>
    )
}

I have tried to follow the steps explained in this tutorial, as well as in that other tutorial, while keeping the layout of the page as simple as possible (no bells & whistles from semantic-ui-css), and no matter what I try, I keep getting into of the following errors:

  • TypeError: Cannot convert undefined or null to object
  • Unexpected token, expected “,”
  • Failed to compile: 'word' is not defined no-undef

The solution explained in this article led me to the code I have now, but there is something I am missing about the way to structure my React app: can you point me in the right direction?

1
  • At the beginning this.state is empty object, so words and word in render are undefined. Also from given info words seems to be an array, so keys would represent index. Does /api/words/${id} return different data than you have in words[0]? Commented Nov 6, 2018 at 6:54

3 Answers 3

2

getWords () {    
  fetch('http://localhost:3001/api/words')
  .then((response) => {
    return response.json();
  })
  .then((res) => {
    // console.log(res); you should get the response you mentioned
    this.setState({words: res});
  });
}

Then check Are you getting data in your state by consoling it.

Then you can work on it using following

render{
 return(
   <div>
     { this.state.words.map((val) => (
         <span>{val.term}</span>
     ))}
   </div>
 )
}

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

Comments

1

The problem is here: let {words, word} = this.state; this.state doesnt have word property yet. You could initialize this.state like this:

this.state = {
   words: [],
   word: {}
};

be free to ask

1 Comment

Evgeniy Boytsov thanks a lot, that contributed to solving the issue related to words and word being undefined.
0

There are two issues with the code in the question:

  1. words & word are not defined.
  2. Iteration through words in the render() function was not set properly with keys.

Thanks to the answers and comments left on this question, here is the code I ended up using:

import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor () {
    super()
    this.state = {
      words : [],
      word : {}
    }
    this.getWords = this.getWords.bind(this)
    this.getWord = this.getWord.bind(this)
  }

  componentDidMount () {
    this.getWords()
  }

  fetch (endpoint) {
    return window.fetch(endpoint)
      .then(response => response.json())
      .catch(error => console.log(error))
  }

  getWords () {
    this.fetch('/api/words')
      .then(words => {
        if (words.length) {
          this.setState({words: words})
          this.getWord(words[0].id)
        } else {
          this.setState({words: []})
        }
      })
  }

  getWord (id) {
    this.fetch(`/api/words/${id}`)
      .then(word => this.setState({word: word}))
  }

  render () {
    let {words} = this.state
    return (
      <ul className="words-container">
        {Object.keys(words).map((key) => {
          return (
            <li className="word-container" key={key}>
              {words[key].term}: {words[key].definition}.
            </li>
          )
        })}
      </ul>
    )
  }
}

export default App;

Comments

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.