0

I'm trying to do a basic API fetch and show that information onClick using a button called GENERATE. All it should do for now is show the first url in the json I receive.

Once that is achieved, I want it to show the next url on each click.

App.js

import React, { Component } from 'react';

import { ThemeProvider, createToolkitTheme } from 'internaltools/theme';
import { AppHeader } from 'internaltools/app-header';


const LIGHT_THEME = createToolkitTheme('light');
const DARK_THEME = createToolkitTheme('dark');
const API = 'https://hn.algolia.com/api/v1/search?query=';
const DEFAULT_QUERY = 'redux';

class App extends Component {
  constructor(props) {
    super(props);
      this.state = {
        hits: [],
        isLoading: false,
        error: null,
      };
  }

  componentDidMount(){
    this.setState({ isLoading: true });

    fetch(API + DEFAULT_QUERY)
      .then(response => {
        if (response.ok) {
          return response.json();
        } else {
          throw new Error('Something went wrong with the API...');
        }
      })
      .then(data => this.setState({ hits: data.hits[0], isLoading: false }))
      .catch(error => this.setState({ error, isLoading: false }));
  }


  render() {
    const { hits, isLoading, error } = this.state;

    return (
        <>
          <button onClick={hits.url}>GENERATE</button>
        </>
    );
  }
}

Please help me find out why my button doesn't work. And how do I iterate over the urls on each click, i.e. show the next url from the json on each click. Thanks.

3
  • What is the button supposed to do? What should happen when you click it? Commented Aug 1, 2019 at 6:47
  • onClick should have a function instead of a property where you will write logic to iterate over the urls. Commented Aug 1, 2019 at 6:49
  • where do you show the url? Commented Aug 1, 2019 at 6:50

4 Answers 4

1

You should pass a function name to your onClick handler. Then in that function you can access the data you wanted.

enter code here
import React, { Component } from 'react';

import { ThemeProvider, createToolkitTheme } from 'internaltools/theme';
import { AppHeader } from 'internaltools/app-header';


const LIGHT_THEME = createToolkitTheme('light');
const DARK_THEME = createToolkitTheme('dark');
const API = 'https://hn.algolia.com/api/v1/search?query=';
const DEFAULT_QUERY = 'redux';

class App extends Component {
  constructor(props) {
  super(props);
  this.state = {
    hits: [],
    isLoading: false,
    error: null,
    hitsCount: 0
  };
  this.handleClick = this.handleClick.bind(this);
}

componentDidMount(){
  this.setState({ isLoading: true });

  fetch(API + DEFAULT_QUERY)
    .then(response => {
      if (response.ok) {
        return response.json();
      } else {
        throw new Error('Something went wrong with the API...');
      }
    })
    .then(data => 
       this.setState({ hits: data.hits, hitsCount: 0 ,isLoading: false 
    }))
     .catch(error => this.setState({ error, isLoading: false }));
    }

  handleClick(){
     this.setState(prevState => ({ hitsCount: prevState.hitsCount + 1 
     }));
  }

  render() {
    const { hits, hitsCount, isLoading, error } = this.state;

    return (
      <>
         <div>
           count: {hitsCount}
           url: {hits[hitsCount].url}
         </div>
         <button onClick={this.handleClick}>GENERATE</button>
      </>
    );
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

This solution doesn't iterate over hits data at all, it just logs the first url.
I was trying to show, that the main problem was not passing function name to the onClick handler. Thanks for your notice. I'm editing now.
0

You need to pass an onClick handler function to update a state value.

Here's a codesandbox that stores the hits array in state along with a current index, and a handler that simply increments the index.

Edit eloquent-mccarthy-ftfcs

Comments

0

Consider This:

Read through the comments in the code to get the updates.

class App extends Component {
  constructor(props) {
    super(props);
      this.state = {
        hits: [],
        currentHit: 0, //add a state currentHit to hold the url that is displayed by now
        isLoading: false,
        error: null,
      };
  }

  componentDidMount(){
    this.setState({ isLoading: true });

    fetch(API + DEFAULT_QUERY)
      .then(response => {
        if (response.ok) {
          return response.json();
        } else {
          throw new Error('Something went wrong with the API...');
        }
      })
      .then(data => this.setState({ hits: data.hits, isLoading: false })) //Make hits array holding all the hits in the response instead of only the first one
      .catch(error => this.setState({ error, isLoading: false }));
  }

  handleClick = () => {
    this.setState(prevState => ({
      currentHit: prevState.currentHit + 1,
    }));
  }


  render() {
    const { hits, isLoading, error, currentHit } = this.state;
    // pass the handleClick function as a callback for onClick event in the button.
    return (
        <>
          <p>{hits[currentHit].url}<p/>
          <button onClick={this.handleClick.bind(this)}>GENERATE</button>
        </>
    );
  }
}

Comments

0

Here is the working code, on each click next url will be shown. codesandbox link

handleChange method can work if you want to append the url from array as well. Or you could just increment the index in this function.

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends React.Component {
  state = {
    data: [],
    index: 0
  };
  componentDidMount() {
    this.setState({ isLoading: true });

    fetch("https://reqres.in/api/users")
      .then(response => {
        if (response) {
          return response.json();
        } else {
          throw new Error("Something went wrong with the API...");
        }
      })
      .then(data => this.setState({ data: data.data }))
      .catch(error => this.setState({ error }));
  }
  handleChange = () => {
    let i =
      this.state.index < this.state.data.length ? (this.state.index += 1) : 0;
    this.setState({ index: i });
  };
  render() {
    return (
      <div className="App">
        <span>
          {this.state.data.length && this.state.data[this.state.index].avatar}
        </span>
        <button onClick={this.handleChange}>GENERATE</button>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

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.