Home > Blockchain >  UseEffect firing on initial render
UseEffect firing on initial render

Time:05-07

I am creating a front end for an API that requires a user to input both an API key as well as a device name. The issue is that the UseEffect() fetch is firing with 'undefined' as the API key on render, so the API is throwing a 401 Error code and preventing the page from rendering so that the user can put in a key.

See below code:

Api.js

const Api = ({ device, key }) => {

    const [data, setData] = useState(null);
    

    useEffect(() => {
        fetch(`APILINK&key=${key}&id=${device}`)
        .then((res) => res.json())
        .then(setData)
    }, [device, key])

    if (data) return (
        <>
            <tbody>
                <tr>
                    <th>Date</th>
                    <th>Temp</th>
                    <th>C02</th>
                </tr>
                {data.samples.map((item) => (
                    <tr>
                        <td>{item.time}</td>
                        <td>{item.data[0]}</td>
                        <td>{item.data[1]}</td>
                    </tr>
                ))}
            
            </tbody>
        </>
    )
    return <div>No Data Found</div>
}

Home.js

const Home = ({ setDevice, setKey }) => {

    const getData = (e) => {
        e.preventDefault();
        const newDevice = e.target.deviceID.value;
        const apiKey = e.target.apiKey.value;
        setDevice(newDevice);
        setKey(apiKey);
    }

    return (
        <>
            <h1>Type in a device and provide an API key below:</h1>
            <form onSubmit={getData}>
                <input type='text' placeholder="Enter Device..." id='deviceID'></input>
                <input style={{display: 'block'}} type='text' placeholder="Enter API Key..." id='apiKey'></input>
                <button>Search</button>
            </form>
        </>

    )
}

export default Home;

App.js

const App = () => {

  const [device, setDevice] = useState()
  const [key, setKey] = useState()

  return (
    <>
      <Home setDevice={setDevice} setKey={setKey} />
      <Api device={device} key={key} />   
    </>
  )
}

export default App;

Any help is appreciated!!

CodePudding user response:

You can wait until the key & device props are available:

useEffect(() => {
    key && device && fetch(`APILINK&key=${key}&id=${device}`)
        .then((res) => res.json())
        .then(setData)
}, [device, key])

useEffect is called right after the first render, regardless of the dependency array, so this protection above will not run the fetch call until those variables are available (when the dependency array "sees" there was a change and calls the useEffect callback).

You might want to show a loader if the key prop might take a while to be available.


I suggest to de-couple the logic which gets the data from the useEffect, because you might want to call getData directly, and it's also better for testing and general code order.

const getData = (key, device) => 
    key && device && fetch(`APILINK&key=${key}&id=${device}`)
        .then((res) => res.json())
        .then(setData);

useEffect(() => {
    getData(key, device)
}, [device, key])

Also, there's no need to fornull in useState(null), because useState() will behave identical.

CodePudding user response:

You can put your fetch inside if statement

useEffect(() => {
        if(key && device){
          fetch(`APILINK&key=${key}&id=${device}`)
          .then((res) => res.json())
          .then(setData)
        }
    }, [device, key])

CodePudding user response:

If your intention is to load data on render and not load more after that, I would make the following changes to your useEffect hook:

 useEffect(() => {
        if(!device || !key || data) return null
        fetch(`APILINK&key=${key}&id=${device}`)
        .then((res) => res.json())
        .then(setData)
    }, [data, device, key])
  • Related