reactjs_user
reactjs_user

Reputation: 169

onClick doesn't work while trying to render: reactjs, basic API fetch

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.

Upvotes: 0

Views: 314

Answers (4)

Sargis
Sargis

Reputation: 11

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>
      </>
    );
  }
}

Upvotes: 1

tarzen chugh
tarzen chugh

Reputation: 11247

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);

Upvotes: 0

Drew Reese
Drew Reese

Reputation: 202605

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

Upvotes: 0

Sultan H.
Sultan H.

Reputation: 2938

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>
        </>
    );
  }
}

Upvotes: 0

Related Questions