Home > database >  JavaScript - React useEffect
JavaScript - React useEffect

Time:12-04

I want to call a component and render it once on button click. So if I pressed the button again it would render however does not constantly try and re render itself.

At the moment, I am passing a function to the component and calling at the end of the useEffect. However this seems to not render anything.

Here is what I have in my App.js

function App() {
  const [open, setOpen] = React.useState(false);
  const [dataFormat, setDataFormat] = React.useState("");

  const openData = () => {
    setOpen(true);
  };

  const closeData = () =>{
    setOpen(false);
  }

  const changeDataFormat = (selectedOption) => {
    console.log(selectedOption);
    setDataFormat(selectedOption);
  };

  return (
    <main className="App">
      <h1>Film Management</h1>
      <SelectDataFormat changeDataFormat={changeDataFormat} />
      <button onClick={openData}>Show Films</button>
      <table border="1">
        <thead>
          <tr>
            <th>Title</th>
            <th>Year</th>
            <th>Director</th>
            <th>Stars</th>
            <th>Review</th>
          </tr>
        </thead>
        <tbody>{open && <FilmTableRows closeData={closeData} dataFormat={dataFormat} />}</tbody>
      </table>
    </main>
  );
}

And this is the component I want to render


function FilmTableRows(props) {
  const convert = require("xml-js");
  const dataFormat = props.dataFormat;
  const [filmList, setFilmList] = useState([]);
  const baseURL = "http://localhost:8080/FilmRestful/filmapi";

  const getJson = () => {
    let config = {
      headers: {
        "data-type": "json",
        "Content-type": "application/json",
      },
    };

    axios
      .get(baseURL, config)
      .then((res) => {
        const resData = res.data;
        setFilmList(resData);
      })
      .catch((err) => {});
  };

  const getXML = () => {
    let config = {
      headers: {
        "data-type": "xml",
        "Content-type": "application/xml",
        // accept: "application/xml",
      },
    };

    axios
      .get(baseURL, config)
      .then((res) => {
        let newList = [];

        const resData = JSON.parse(
          convert.xml2json(res.data, { compact: true, spaces: 2 })
        );

        resData.films.film.forEach((f) => {
          const film = new Film(
            f.id,
            f.title,
            f.year,
            f.director,
            f.stars,
            f.review
          );
          newList = newList.concat(film);
        });

        setFilmList(newList);
      })
      .catch((err) => {});
  };

  const getString = () => {
    let config = {
      headers: {
        "data-type": "string",
        "Content-type": "application/html",
        // accept: "application/xml",
      },
    };
    axios
      .get(baseURL, config)
      .then((res) => {
        setFilmList(res.data);
      })
      .catch((err) => {});
  };

  useEffect(() => {
    switch (dataFormat.value) {
      case "json":
        getJson();
        break;
      case "xml":
        getXML();
        break;
      default:
        getString();
    }
  });

  const child = filmList.map((el, index) => {
    return (
      <tr key={index}>
        <td>{el.title}</td>
        <td>{el.year}</td>
        <td>{el.director}</td>
        <td>{el.stars}</td>
        <td>{el.review}</td>
      </tr>
    );
  });

  return <>{filmList && child}</>;
}

CodePudding user response:

Here is an Example Code

import * as React from 'react';

function App() {
  //toggle the state (false by default)
  const [toggle, setToggle] = React.useState(false)

  //Component to be displayed on button click
  const FilmTableRows  = () =>{
    return(
      <div>
        <h1>Film</h1>
        <p>FilmTableRows</p>
      </div>
    )
  }
  return (
    <div>
      <h1>Hello StackBlitz!</h1>
      {/* basically switches the value of toggle on every click */}
      <button onClick={() => setToggle(!toggle)}>Click Me</button>
      {toggle && <FilmTableRows/>}
    </div>
  );
}
export default App

CodePudding user response:

Quick fix

 const [reload, setReload] = useState(false);

 // toggle reload 
 const refresh = () => {
    setReload(r => !r);
 };

  const openData = () => {
       setOpen(true);
       // if you want the same button "Show Films" to refersh
       if (open) {
           refresh();
       }
  };

 return (
  ...
  <button onClick={toggleOpen}>Show Films</button>
  // if you want to use seperate refresh button
  //  <button onClick={refresh}>Refresh</button>
  ...
   <tbody>{open && <FilmTableRows reload={reload} closeData={closeData} dataFormat={dataFormat} />}</tbody>
 

Inside FilmTableRows use reload to trigger fetch

 // include depedency to stop fetching on every render
  useEffect(() => {
    switch (dataFormat.value) {
      case "json":
        getJson();
        break;
      case "xml":
        getXML();
        break;
      default:
        getString();
    }
  }, [dataFormat.value, props.reload]);
  • Ideally you should move the fetch function in the parent, so that on refresh fetch data can be called from the event handler

  • You can also use a filter state instead of reload flag and use the filter as a param when making an api call

Hope it helps you towards finding better solution, you can also read the new React beta docs, they will help you to write better code.

Cheers

  • Related