0

I have a page file in my React app that has a sidebar component.

import React, { Component } from 'react';
import Navbar from './../components/layout/Navbar';
import { Table } from './../components/layout/Table';
import { Sidebar } from './../components/layout/Sidebar';

export class LandingPage extends Component {
    render() {
        return (
            <div>
                <Navbar />
                <h2 >Affiliation Information</h2>
                <div className="wrapper">
                    <Table />
                    <Sidebar />
                </div>
            </div>
        )
    }
}

export default LandingPage

This sidebar component consists of an input textbox and a submit button. The idea here is that the user types something into the textbox (Ex: "La") triggering an event handler that sets the state of const interest then clicks submit. The submit button then plugs the interest state value into a url string for a call to my API using Axios, which is suppose to return the query results of any items where name is like "La". (Ex: [{"contact_name": "John Latham"}]).

import React, { useState, useEffect } from 'react';
import config from '../../config/config';
import axios from "axios";

export const Sidebar = () => {
    const [interest, setInterest] = useState(null);
    const [response, setResponse] = useState([]);
    const [loadingData, setLoadingData] = useState(true);
    console.log("At top: " + config.apiURL + `/affiliations/full?name=${interest}`);

    function HandleChange(event) {
        setInterest(event.target.value);
    };

    async function callYourAPI(interest) {
        await axios
            .get(config.apiURL + `/affiliations/full?name=${interest}`)
            .then((res) => {
                setResponse(res.data);
                setLoadingData(false);
            })
            .catch(function (error) {
                console.log(error);
            });
    };

    useEffect(() => {
        if (loadingData) {
            callYourAPI();
        } else {
            console.log(response);
        }
    }, [loadingData, response]); // Makes the useEffect dependent on response.

    return (
        <div className="sidebar" style={{
            borderLeft: "1px solid gray"
        }}>
            <h2 style={{ textAlign: "center" }}> Filter</h2>
            <form>
                <label style={{}}>Name</label> <br />
                <input
                    placeholder="Type name"
                    style={{ textAlign: "center", marginRight: "10px", height: "25px", width: "200px" }}
                    value={interest}
                    onChange={HandleChange}></input>
                <button className="btn btn-primary" onClick={() => callYourAPI(interest)}> Search</button>
            </form>
        </div >
    )
}

However, what happens is that when I initially type in a value in the input box, its sets the state correctly, but when I click the button, everything goes haywire! The interest variable becomes undefined (the box gets cleared) and the process seems to loop through the Sidebar() function about 3 times ( and the sync callYourAPI() function) before it finishes but never returns the console.log(response); in my useEffect() function. At this point I am confused; what am I doing wrong here?!

UPDATE: I have made the recommended changes, using e.preventDefault() in my forms onSubmit. However, it seems like this is called on load, and its stuck in some kind of infinite call loop to the API.

export const Sidebar = () => {
    const [interest, setInterest] = useState("");
    const [response, setResponse] = useState([]);
    const [loadingData, setLoadingData] = useState(true);

    function HandleChange(event) {
        setInterest(event.target.value);
    };

    async function callYourAPI(interest) {
        await axios
            .get(config.apiURL + `/affiliations/full?name=${interest}`)
            .then((res) => {
                setResponse(res.data);
                setLoadingData(false);
            })
            .catch(function (error) {
                console.log(error);
            });

    };

    return (
        <div className="sidebar" style={{
            borderLeft: "1px solid gray"
        }}>
            <h2 style={{ textAlign: "center" }}> Filter</h2>
            <form onSubmit={(e) => { e.preventDefault(); callYourAPI(interest); }}>
                <label style={{}}>Name</label> <br />
                <input
                    placeholder="Type name"
                    style={{ textAlign: "center", marginRight: "10px", height: "25px", width: "200px" }}
                    value={interest}
                    onChange={HandleChange}></input>
                <button className="btn btn-primary"> Search</button>
            </form>
        </div >
    )
}
gwydion93
  • 1,681
  • 3
  • 28
  • 59
  • Does this answer your question? [How to prevent buttons from submitting forms](https://stackoverflow.com/questions/932653/how-to-prevent-buttons-from-submitting-forms) – Emile Bergeron Jun 15 '21 at 17:19
  • 1
    Or specifically for React: [React - Preventing Form Submission](https://stackoverflow.com/q/39809943/1218980) – Emile Bergeron Jun 15 '21 at 17:21
  • Question: The above example looks promising. How would you replace the constructor (ex: `this.formSubmit = this.formSubmit.bind(this);`) in my `Sidebar` function? – gwydion93 Jun 15 '21 at 19:35
  • 1
    Binding the context is unnecessary in a function component. More info on [the context (`this`)](https://stackoverflow.com/q/3127429/1218980) – Emile Bergeron Jun 15 '21 at 20:19
  • 1
    If you really want to use a form, just move the `callYourAPI` usage to `
    { e.preventDefault(); callYourAPI(interest); }}>`
    – Emile Bergeron Jun 15 '21 at 20:21
  • OK, I have made the changes suggested and now my app is stuck in an API call loop or something. It seems as if it tries to call the API before I ever click/submit the form. Making an update to the above code example. – gwydion93 Jun 15 '21 at 20:32

1 Answers1

1

Try adding event.preventDefault() like this:

function HandleChange(event) {
        event.preventDefault()
        setInterest(event.target.value);
    };

The default behavior when submitting a form is to reload the page, therefore resetting the state. event.preventDefault() prevents the default behavior of the page.

  • No, it is still reloading the page and wiping the state. Is there somewhere else I need to add `preventDefault` (like the button click event?)? Could I somehow move the `callYourAPI` function inside `useEffect` and only make it dependent upon the response? – gwydion93 Jun 15 '21 at 18:09
  • 1
    Oh, sorry... my bad. You need to add the `event.preventDefault()` on your callYourAPI function. The button arrow function will be like this: `` Then in the function: `async function callYourAPI(interest, event) { event.preventDefault(); * * * ` – Пламен Дарджиков Jun 15 '21 at 19:54
  • 1
    OK, I was able to get it to work eventually, but used `onSubmit={(e) => { e.preventDefault(); callSearchAPI(interest); }}` inside the form tag (I changed the name of the axios function to `callSearchAPI`). Thanks for the assist! – gwydion93 Jun 16 '21 at 13:52