Why is the API response not being rendered by useState? - reactjs

I am trying to render data fetched from an API using axios but nothing renders on screen. Note that data is actually available as indicated on the console log. Here is the code and what I have tried.
import React, { useState, useEffect } from "react";
import axios from "axios";
function Test() {
const [movie, setMovie] = useState([]);
const [query, setQuery] = useState("pulp fiction");
const [queryFromButtonClick, setQueryFromButtonClick] = useState(
"pulp fiction"
);
const handleClick = () => {
setQueryFromButtonClick(query);
};
useEffect(() => {
axios
.get(`http://www.omdbapi.com/?apikey=fd010aa6&s=${queryFromButtonClick}`)
.then(({ data }) => {
console.log(data);
setMovie(data.Search);
});
}, [queryFromButtonClick]);
return (
<div>
<input
type="text"
value={query}
onChange={(e) => {
setQuery(e.target.value);
}}
/>
<button onClick={handleClick}>Fetch movies</button>
<div>{movie.Title}</div>
</div>
);
}
export default Test;
Why are the search query results not being rendered on screen and how can I go on about that?

Movie is defined as an array and apparently data.Search is also an array.
You need to iterate over movie array to get the data about each movie.
Like this:
return (
<div>
<input
type="text"
value={query}
onChange={(e) => {
setQuery(e.target.value);
}}
/>
<button onClick={handleClick}>Fetch movies</button>
<div>{movie.map((el)=>el.Title)}</div>
</div>
);

Related

How to handle multiple select options submittion in react js?

I want to submit a form into mongoDB using nodejs API & reactJs. With the exception of the multiple select option, everything is operating as it should be.
Being new to react, I have no idea how to handle the multi select option's onChange method.
Here is what I've tried:
import React, { useState, useRef } from "react";
import { useForm } from "react-hook-form";
import { v4 as uuidv4 } from 'uuid';
import axios from "axios";
import Select from 'react-select';
export default function EventForm(props) {
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm();
const form = useRef();
const [loading, setLoading] = useState(false);
const [info, setInfo] = useState("");
const [analysis, setAnalysis] = useState("Undefined");
const [relatedEvent, setRelatedEvent] = useState([]);
const handleInfoChange = (e) => {
setInfo(e.target.value)
}
const handleAnalysisChange = (e) => {
setAnalysis(e.target.value)
}
const handleRelatedEvents = (e) => {
setRelatedEvent(e.target.value)
}
const relatedEventsData = props.data.map(opt => ({ label: opt.info, value: opt._id }));
const onSubmit = async () => {
setLoading(true);
const MySwal = withReactContent(Swal);
const eventData = {
UUID: uuidv4(),
info: info,
analysis: analysis,
relatedEvent: relatedEvent,
}
axios
.post(`${process.env.REACT_APP_PROXY}/api/events`, eventData)
.then((res) => {
console.log(res);
setLoading(false);
MySwal.fire(
"Success!",
"A new event has been saved successfully",
"success"
);
})
.catch((error) => {
console.log(error);
});
};
return (
<div className="panel-body">
<Form
ref={form}
onSubmit={handleSubmit(onSubmit)}
className="form-horizontal"
>
<div className="row">
<div className="col-lg-6">
<div className="mb-3">
<Form.Label>Info</Form.Label>
<Form.Control
type="text"
placeholder="Enter info..."
{...register("info", { required: true })}
value={info}
onChange={handleInfoChange}
/>
{errors.info && (
<ul className="parsley-errors-list filled" id="parsley-id-7" aria-hidden="false">
<li className="parsley-required">This value is required.</li>
</ul>
)}
</div>
</div>
<div className="col-lg-6">
<div className="mb-3">
<Form.Label>Related events</Form.Label>
<Select
options={relatedEventsData}
value={relatedEvent}
isMulti
onChange={handleRelatedEvents}
/>
</div>
</div>
<div className="col-lg-12">
<Button variant="primary" type="submit">
{loading ? "Saving..." : "Save"}
</Button>
</div>
</div>
</Form>
</div>
);
}
Could you please guide me how to make it work!
Thank you
you can make use of Select onChange event handler which passes the selected options as an array as argument ..
from that you can map over it to get the values as required
something as below:
const handleChange = (opts) => {
const selectedValues = opts.map((opt) => opt.value);
setSelectedValues(selectedValues);
};
Please check the working sample for better clarity 😉 -

react js myfn is not a function when called from a button

I've just started learning about react js and this is my first react js app. I'm using api to fetch the data. so far it works, but now I want to add a search keyword to the function that is acquired from a search bar component.
here's my code:
SearchBar.js
const SearchBar = ({ getUsers }) => {
return (
<div className="is-flex flex-align-items-center mb-3">
<input type="text" id="query" className="input search-input" placeholder="search keyword"/>
<Button className="search-btn ps-3 pe-3"
onClick={() => getUsers(document.querySelector('#query').value)}>
<FontAwesomeIcon icon={faMagnifyingGlass} />
</Button>
</div>
);
};
MasterUser.js
import { useState, useEffect } from "react";
import SearchBar from "./SearchBar";
const MasterUser = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
getUsers();
}, []);
const getUsers = async (query='') => {
console.log('get users', query);
try {
let myurl = 'http://localhost:8080/users';
const response = await fetch(myurl);
const data = await response.json();
setUsers(data);
setIsLoading(false);
} catch (e) {
console.log(e.getMessage());
}
};
return (
<div>
<SearchBar onClick={getUsers}/>
</div>
);
};
when the app loads, the console log says get users <empty string> and it returns all the users as expected, but when I clicked on the search button (magnifyingGlass) it gives an error Uncaught TypeError: getUsers is not a function.
any help is appreciated..
<SearchBar onClick={getUsers}/>
You have named the prop onClick not getUsers. That's why you get that error.
Yeah, accessing dom element value using selectors (e.g. document.querySelector('#query').value) is also not typical react. Read about controlled form elements (save form element value in state).
Make your searchBar component more reactive like so
const SearchBar = ({ getUsers }) => {
const [searchValue,setSearchValue]=useState('');
return (
<div className="is-flex flex-align-items-center mb-3">
<input type="text" id="query" className="input search-input" placeholder="search keyword" value={searchValue} onChange={(e)=>setSearchValue(e.target.value)}/>
<Button className="search-btn ps-3 pe-3"
onClick={() => getUsers(searchValue)}>
<FontAwesomeIcon icon={faMagnifyingGlass} />
</Button>
</div>
);
};

How do I edit form data in a React function component?

I'm trying to set a form field value with useState.
The settings.values.apiKey variable has a value, but the textarea element is empty. What's wrong with my useState?
I tried to change value={apiKey} to value={settings.values.apiKey} and then the value is displayed, but then I can't change the value of the field. When I try to enter something, it always shows the original value.
App.js
const App = () => {
const [apiKey, setApiKey] = useState(settings.values.apiKey)
useEffect(() => {
const getSettings = async () => {
const settingsFromServer = await fetchSettings()
setSettings(settingsFromServer)
}
getSettings()
}, [])
const fetchSettings = async () => {
const res = await fetch('http://127.0.0.1/react-server/get.php')
return await res.json()
}
const saveSettings = async (settings) => {
}
return (
<div className="container">
<Header />
<Settings
settings={settings}
saveSettings={saveSettings}
/>
<Footer />
</div>
);
}
export default App;
Settings.js:
import { useState } from 'react';
const Settings = ({ settings, saveSettings }) => {
const [apiKey, setApiKey] = useState(settings.values.apiKey)
const onSubmit = (e) => {
e.preventDefault()
saveSettings({ apiKey})
}
return (
<div>
<form className='add-form' onSubmit={onSubmit}>
<div className='form-control'>
<label>Api key</label>
<textarea
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
/>
</div>
<input type='submit' value='Save settings' className='mt15' />
</form>
</div>
)
}
export default Settings
It looks like by mistake you have used apiKey in App.js file as your state variable. It should be replaced by settings.
const [settings, setSettings] = React.useState();
The above code would make value={apiKey} work properly for textarea in Settings.js file.
And, then onChange will also start working properly.
UPDATE
In addition to the above mentioned error, in case settings props is undefined in Settings.js, this might cause your code to break at useState. So, instead put a check for settings values in useEffect and then set the value. The code would look like this or you can check the codesandbox link here for working demo.
Settings.js
import { useEffect, useState } from "react";
const Settings = ({ settings, saveSettings }) => {
const [apiKey, setApiKey] = useState();
useEffect(() => {
if (settings?.values?.apiKey) {
setApiKey(settings.values.apiKey);
}
}, [settings]);
const onSubmit = (e) => {
e.preventDefault();
saveSettings({ apiKey });
};
return (
<div>
<form className="add-form" onSubmit={onSubmit}>
<div className="form-control">
<label>Api key</label>
<textarea
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
/>
</div>
<input type="submit" value="Save settings" className="mt15" />
</form>
</div>
);
};
export default Settings;
App.js
const [settings, setSettings] = useState()
const saveSettings = async (settings) => {
setSettings(settings);
}

ReactJs .map in data received by api

and thank you in advance for any help.
I am trying to build a web app that fetches data from an API, in this case a movie database API, but when i am trying to map all the movies from a specific title search i get the .map is not a function error, what i am doing wrong ? Can't i use useState to display the data ?
When i do console.log (search) i can see the array with all the data :
import React, {useEffect, useState} from 'react';
import axios from 'axios';
export default function RandomFacts() {
const [input, setInput] = useState('');
const [search, setSearch] = useState(['']);
useEffect(() => {
apiCall();
}, [input]);
const moviesList = search && search.map((movie, index) =>
<div className="movies" key="index">
<li><h2>{movie.Title}</h2></li>
<li><img src={movie.Poster} alt="poster" /></li>
</div>,
);
const apiCall = async () => {
const url = 'http://www.omdbapi.com/?s='+input+'&page=1&apikey=536a34c3';
try {
const response = await axios.get(url);
if (response.status === 200 && response !== undefined) {
const data = response.data;
setSearch(data.Search);
console.log(search);
}
} catch (error) {
console.log(error);
}
};
return (
<div className="main">
<h1>Movies</h1>
<div className="textInput">
<form>
<label>
<input type="text" value={input}
onChange={(e) => setInput(e.target.value)}
/>
</label>
</form>
</div>
<div className="movies">
{moviesList}
</div>
</div>
);
}
The API is returning a response Object with a data key containing the keys Search, TotalResults, and Response. You're trying to map this response Object instead of the Array contained in response.data.Search.
So you should be using setSearch(response.data.Search).

Issue using React Hooks on an axios call

I'm making a basic weather app with React, and having an issue getting my setWeather to update weather. I had read that setState doesn't update state the first time it's called, and that seems consistent with the empty object that console.log(weather) returns. cityData returns the full response, as expected, but weather.name and non-nested data (i.e. only strings, not arrays or objects) functions properly, which is unexpected.
I would like to know how to get setWeather to perform as advertised, and why the arrays and objects that the API return are showing as undefined.
import React, { useState } from 'react';
import axios from 'axios';
const Search = () => {
const [query, setQuery] = useState('');
const [weather, setWeather] = useState({});
const findCity = (e) => {
e.preventDefault()
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${query}&units=imperial&appid=${APIKEY}`)
.then(res => {
const cityData = res.data;
console.log(cityData);
setWeather(res.data);
setQuery('');
console.log(weather)
}).catch(err => console.log(err))
}
return(
<React.Fragment>
<h1>App</h1>
<p>Get the weather in your city!</p>
<form onSubmit={findCity}>
<input
type='text'
className='city-search'
placeholder='What city are you looking for?'
name='city-name'
onChange={e => setQuery(e.target.value)}
value={query}
/>
<button
type='submit'>
Get City
</button>
</form>
<h1>{weather.name}</h1>
</React.Fragment>
)
}
You won't be able to do console.log(weather) in the submit handler because the submit handler is still using the old weather (i.e. from current render). Do this instead:
const Search = () => {
const [query, setQuery] = useState('');
const [weather, setWeather] = useState({});
const findCity = (e) => {
e.preventDefault()
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${query}&units=imperial&appid=${APIKEY}`)
.then(res => {
const cityData = res.data;
console.log(cityData);
setWeather(res.data);
setQuery('');
}).catch(err => console.log(err))
}
console.log(weather) //<-- THIS IS THE ONLY THING I'VE CHANGED
return(
<React.Fragment>
<h1>App</h1>
<p>Get the weather in your city!</p>
<form onSubmit={findCity}>
<input
type='text'
className='city-search'
placeholder='What city are you looking for?'
name='city-name'
onChange={e => setQuery(e.target.value)}
value={query}
/>
<button
type='submit'>
Get City
</button>
</form>
<h1>{weather.name}</h1>
</React.Fragment>
)
}
https://api.openweathermap.org/data/2.5/weather?q=${query}&units=imperial&appid=${APIKEY}
Are you passing the query and APIKEY here. If not please add them as well to your axios call. Am assuming that your getting an invalid response. APIKEY has to be provided to get a successful response from the Weather API.

Resources