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