How to implement fetch API by search - reactjs

I have a search bar. when I enter the name of any film, filtered data has to be displayed (as a result of fetching from API)
function App() {
const [films, setFilms] = useState([])
const [searchText, setSearchText] = useState('')
const [filteredRes, setFilteredRes] = useState([])
const url = `http://www.omdbapi.com/?apikey=KEY&s=${searchText}`
useEffect (()=> {
loadData()
}, [searchText])
const loadData = async () => {
const res = await axios.get(url)
setFilms(res.data.Search)
const filtered = await films.filter(i => i.toLowerCase().includes(searchText))
setFilteredRes(filtered)
}
const onTextChange = (e) => {
setSearchText(e.target.value)
}
return (
<>
<Container>
<h1>Bookstore</h1>
<Row>
<form>
<input type='text'
placeholder='Search...'
name="searchText"
onChange={onTextChange}
value={searchText}
/>
</form>
</Row>
<Row>
{ filteredRes.map(item => {
return (
<Col lg={3} md={3} sm={12} key={item.imdbID}>
<img src={item.poster}/>
<h6>{item.title}</h6>
<h6>{item.year}</h6>
</Col>
)
})}
</Row>
</Container>
</>
);
}
I have am issue:
Unhandled Rejection (TypeError): Cannot read property 'filter' of undefined. But why if I save fetched films in const [ films ] ?

Ooutput:
You don't have to filter fetched data additionally, the data that is being fetched by Axios is already filtered according to the serchText so Axios call does the all.
Full Example:
import React, { useState } from "react";
import Axios from "axios";
const APIKEY = "your api";
export default function App() {
const [searchText, setSearchText] = useState("");
const [films, setFilms] = useState([]);
const handleSubmit = (event) => {
event.preventDefault();
if (!searchText) {
alert("Please enter movie name");
return;
}
Axios.get(
`http://www.omdbapi.com/?i=tt3896198&apikey=${APIKEY}&s=${searchText}`
)
.then((response) => {
console.log(response.data);
setFilms(response.data.Search);
})
.catch((error) => {
console.log(error);
});
};
return (
<div className="App">
<form onSubmit={handleSubmit}>
<input
placeholder={"Search movie"}
onChange={(event) => {
setSearchText(event.target.value);
}}
/>
<button>Search</button>
</form>
{films.map((film) => (
<div>
<img src={film["Poster"]} alt={film["Title"]} width={100} />
<p>{film["Title"]}</p>
<p>{film["Year"]}</p>
</div>
))}
</div>
);
}
Github Repo

Related

Enter the selected item in the search bar in React.js

I have the following code:
function App() {
const [countries,setCountries]= useState([]);
const [search, setSearch] = useState('');
//Take data from API with useEffect, async/await and try/catch
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('https://restcountries.com/v2/all');
setCountries(response.data);
} catch (error) {
console.error(error);
}
}
fetchData();
}, []);
const filteredCountries = countries.filter((country) =>
country.name.toLowerCase().includes(search.toLowerCase())
);
const handleSelect = (country) => {
setSearch(country.name);
}
return (
<>
<div>
<SearchBar onChange={(e)=> setSearch(e.target.value)} />
{
<ul className="list">
{search.length > 0 && filteredCountries.map((country) => (
<li key={country.name} onClick={() => handleSelect(country)}>
{country.name}
</li>
))}
</ul>
}
</div>
<div className="map-container">
</div>
</>
)
}
export default App;
The result is this:
List image
How can I select an item from the list, e.g. if I search for Ital, Italy appears and I would like to select it and have it appear in the search bar.
I would like to create a search bar to find a country and select it, it should appear in the search bar after being selected.
CodeSandBox Link: https://codesandbox.io/p/github/pierre1590/Population-Tracker/draft/gallant-gagarin?file=%2Fsrc%2Fcomponents%2FMap%2FMap.js
Add value={search} in your <SearchBar/> component.
eg: <SearchBar value={search} onChange={(e)=> setSearch(e.target.value)} />
Below is the full code (I've used a normal input tag in place of your SearchBar component)
import { useState, useEffect } from "react";
import axios from 'axios';
function App() {
const [countries,setCountries]= useState([]);
const [search, setSearch] = useState('');
console.log(search)
//Take data from API with useEffect, async/await and try/catch
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('https://restcountries.com/v2/all');
setCountries(response.data);
} catch (error) {
console.error(error);
}
}
fetchData();
}, []);
const filteredCountries = countries.filter((country) =>
country.name.toLowerCase().includes(search.toLowerCase())
);
const handleSelect = (country) => {
setSearch(country.name);
}
return (
<>
<div>
<input value={search} onChange={(e)=> setSearch(e.target.value)} />
{
<ul className="list">
{search.length > 0 && filteredCountries.map((country) => (
<li key={country.name} onClick={() => handleSelect(country)}>
{country.name}
</li>
))}
</ul>
}
</div>
<div className="map-container">
</div>
</>
)
}
export default App;
CodeSandBox Link - https://codesandbox.io/s/enter-the-selected-item-in-the-search-bar-in-react-js-582rez?file=/src/App.js

How to upload image file using ReactJS to api using NestJS with bytea datatype

I want to know how can I properly upload a file using ReactJS to api using NestJS, so far here is what I have done
in API's swagger, here is the post method for file upload
This is the table from PostgreSQL for that entity
The api is created using this crud typeorm #nestjsx/crud-typeorm
And in my ReactJS code, currently I have this page:
const EditBadge = () => {
const { id } = useParams();
const history = useNavigate();
const [data, setData] = useState({
id: "",
badge_name: "",
badge_description: "",
img_icon: "",
});
const [baseImage, setBaseImage] = useState("");
const { badge_name, badge_description, img_icon } = data;
const onInputChange = (e: any) => {
setData({ ...data, [e.target.name]: e.target.value });
};
const onSubmit = async (e: any) => {
e.preventDefault();
await api.patch(`badges/${id}`, data);
history("/badge");
};
const loadData = async () => {
const result = await api.get(`badges/${id}`);
setData(result.data);
};
useEffect(() => {
loadData();
}, []);
const uploadImage = async (e: any) => {
const file = e.target.files[0];
const base64 = await convertBase64(file);
setBaseImage(String(base64));
};
const convertBase64 = (file: any) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = () => {
resolve(fileReader.result);
};
fileReader.onerror = (error) => {
reject(error);
};
});
};
return (
<Container className="create-badge-container">
<Container className="create-badge-content">
<Row>
<Col>
<div className="img-container text-center">
<Image
className="p-2"
src={baseImage}
alt=""
width={198}
height={219}
/>
</div>
</Col>
</Row>
<Row>
<Col>
<Form className="create-badge-form" onSubmit={(e) => onSubmit(e)}>
<Row className="pt-4">
<Col className="col-4">
<input
type="text"
className="form-control"
placeholder="Badge Name"
aria-label="Badge Name"
name="badge_name"
value={badge_name}
onChange={(e) => onInputChange(e)}
/>
</Col>
<Col className="col-4">
<input
className="form-control"
type="file"
id="file"
accept=".jpeg, .png, .jpg"
name="img_icon"
onChange={(e) => {
uploadImage(e);
}}
/>
</Col>
</Row>
<Row className="pt-4">
<Col className="col-8">
<textarea
className="form-control"
id="exampleFormControlTextarea1"
placeholder="Description"
rows={3}
name="badge_description"
value={badge_description}
onChange={(e) => onInputChange(e)}
></textarea>
</Col>
</Row>
<Row className="pt-5">
<Col className="col-4">
<Button type="submit" className="btn-create">
Update
</Button>
</Col>
</Row>
</Form>
</Col>
</Row>
</Container>
</Container>
);
};
export default EditBadge;
So far this is what I've got base on research, and I got stuck and have no idea how can I post/create this with the uploaded image file
I don't know how you prefer to do things me in my case I used axios and the FormData class
my reactjs code
import React,{ ChangeEvent, useState } from "react";
import axios from "axios";
const UploadFile = () => {
const [file, setFile] = useState<any>();
const onChange = (file: ChangeEvent) => {
const { files } = file.target as HTMLInputElement;
if (files && files.length !== 0) {
setFile(files[0]);
}
}
const handleUpload = async () => {
const formData = new FormData();
formData.append('file', file)
const upload = await axios({
url:"http://localhost:4000/upload",
method:"post",
headers:{
Authorization: `Bearer your token`
},
data:formData
}).then(r => r);
console.log(upload);
}
return (
<div>
<form onSubmit={e => e.preventDefault()}>
<input type="file" onChange={onChange} />
<button onClick={handleUpload}>upload</button>
</form>
</div>
)
}
export default UploadFile
and my nestjs code
import { Controller, Post, UploadedFile, UseGuards, UseInterceptors } from "#nestjs/common";
import { diskStorage } from "multer";
import { randomUUID } from 'crypto';
import Path = require('path');
import { FileInterceptor} from '#nestjs/platform-express';
import { JwtAuthGuard } from "src/auth/guard/jwt.auth.guard";
const storage = {
storage : diskStorage({
destination: 'src/uploads/files',
filename: (req, file, cb) =>{
const filename: string = 'myfile-' + randomUUID();
const extension: string = Path.parse(file.originalname).ext;
cb(null, `${filename}${extension}`)
}
})
}
#Controller('upload')
export class UploadController {
// #UseGuards(JwtAuthGuard) your methode of guard
#Post()
#UseInterceptors(FileInterceptor('file', storage))
uploaiFile(
#UploadedFile() file:any
){
console.log(file)
return file
}
}

Cannot get token

I wanna make a function that, When i submit the form i want to send the username and room name to the server to exchange for an access token, and if there's a token, my Room component will be rendered and you can see the roomName being rendered. However when i click the submit button, nothing happened. What's wrong with my codes?
VideoChat.js:
import React, { useState, useCallback } from "react";
import Lobby from "./Lobby";
import Room from "./Room"
function VideoChat() {
const [username, setUsername] = useState("");
const [roomName, setRoomName] = useState("");
const [token, setToken] = useState(null);
const handleUsernameChange = useCallback((event) => {
setUsername(event.target.value);
}, []);
const handleRoomNameChange = useCallback((event) => {
setRoomName(event.target.value);
}, []);
const handleSubmit = useCallback(
async event => {
event.preventDefault();
const data = await fetch("/video/token", {
method: "POST",
body: JSON.stringify({
identity: username,
room: roomName,
}),
headers: {
"Content-Type": "application/json",
},
}).then((res) => res.json());
setToken(data.token);
},
[username, roomName]
);
const handleLogout = useCallback((event) => {
setToken(null);
}, []);
Lobby.js:
import React from "react";
const Lobby = ({
username,
handleUsernameChange,
roomName,
handleRoomNameChange,
handleSubmit
}) => {
return (
<form onSubmit={handleSubmit}>
<h2> Enter a room </h2>{" "}
<div>
<label htmlFor="name"> Name: </label>{" "}
<input
type="text"
id="field"
value={username}
onChange={handleUsernameChange}
required
/>
</div>{" "}
<div>
<label htmlFor="room"> Room name: </label>{" "}
<input
type="text"
id="room"
value={roomName}
onChange={handleRoomNameChange}
required
/>
</div>{" "}
<button type="submit" > Submit </button>{" "}
</form>
);
};
export default Lobby;
return (
<div>
{token ? (<div><Room roomName={roomName} token={token} handleLogout={handleLogout} /></div>)
: (<div>
<Lobby
username={username}
roomName={roomName}
handleUsernameChange={handleUsernameChange}
handleRoomNameChange={handleRoomNameChange}
handleSubmit={handleSubmit}
/>
</div>)
}
</div>
);
}
export default VideoChat;
Room.js:
import React, { useState, useEffect } from "react";
import Video from "twilio-video";
const Room = ({ roomName, token, handleLogout }) => {
const [room, setRoom] = useState(null);
const [participants, setParticipants] = useState([]);
const remoteParticipants = participants.map(participant=> {
<p key={participant.sid}>{participant.identity}</p>
})
useEffect(() => {
const participantConnected = participant => {
setParticipants(prevParticipants => [...prevParticipants, participant]);
};
const participantDisconnected = participant => {
setParticipants(prevParticipants =>
prevParticipants.filter(p => p !== participant)
);
};
Video.connect(token, {
name: roomName
}).then(room => {
setRoom(room);
room.on('participantConnected', participantConnected);
room.on('participantDisconnected', participantDisconnected);
room.participants.forEach(participantConnected);
});
});
return (
<div className="room">
<h2>Room: {roomName}</h2>
<button onClick={handleLogout}>Log out</button>
<div className="local-participant">
{room ? (<p key={room.localParticipant.sid}>{room.localParticipant.identity}</p>) : ''}
</div>
<h3>Remote Participants</h3>
<p>{remoteParticipants}</p>
</div>
)
};
export default Room;
Sandbox link: https://codesandbox.io/s/video-chat-y67nv?file=/src/Lobby.js

How should I fetch data onClick in Next js?

I have an input field plus a button next to it. I want to fetch data whenever the client presses the button.
I used SWR (I'm not sure if I have to use SWR or getServerSideProps method)
the problem is it fetches data when I refresh the page.
here is my code:
const fetcher = async () => {
const res = await fetch(
'https://eu-central-1.aws.webhooks.mongodb-realm.com/api/client/v2.0/app/lottigully-jjrda/service/movies/incoming_webhook/movies?arg=dexter'
);
const data = await res.json();
return data;
};
const { data, error } = useSWR('uk', fetcher);
if (error) {
return 'there was an error';
}
if (!data) {
return 'Loading';
}
console.log(data);
return (
<>
<main>
<div className={style.main_container}>
<NavBar />
<Hero />
</div>
<div className={style.search_container}>
<SearchBar
onChange={(e) => {
setSearchTerm(e.target.value);
console.log(searchTerm);
}}
/>
</div>
<button onClick={?????}>Search!</button>
</main>
</>
);
}
You should call mutate function that useSWR returns you.
const fetcher = async () => {
const res = await fetch(
'https://eu-central-1.aws.webhooks.mongodb-realm.com/api/client/v2.0/app/lottigully-jjrda/service/movies/incoming_webhook/movies?arg=dexter'
);
const data = await res.json();
return data;
};
const { data, mutate, error } = useSWR('uk', fetcher);
if (error) {
return 'there was an error';
}
if (!data) {
return 'Loading';
}
console.log(data);
return (
<>
<main>
<div className={style.main_container}>
<NavBar />
<Hero />
</div>
<div className={style.search_container}>
<SearchBar
onChange={(e) => {
setSearchTerm(e.target.value);
console.log(searchTerm);
}}
/>
</div>
<button onClick={() => mutate()}>Search!</button>
</main>
</>
);
}
Check this link please https://swr.vercel.app/docs/mutation
You can do something like this:
import { useState } from 'react';
import useSWR from 'swr';
const fetcher = (...args) => fetch(...args).then((res) => res.json());
const SearchResults = ({ keyword }) => {
const { data, error } = useSWR(
`https://eu-central-1.aws.webhooks.mongodb-realm.com/api/client/v2.0/app/lottigully-jjrda/service/movies/incoming_webhook/movies?arg=${keyword}`,
fetcher
);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
if (!data[0]) return <div>not found</div>;
return <div>found {data[0].name}</div>;
};
const Home = () => {
const [startFetching, setStartFetching] = useState(false);
const [searchTerm, setSearchTerm] = useState('');
const handleChange = (e) => {
setStartFetching(false);
setSearchTerm(e.target.value);
};
const handleClick = () => {
setStartFetching(true);
};
return (
<>
<label for="series">Series: </label>
<input type="text" value={searchTerm} onChange={handleChange} id="series" />{' '}
<input type="submit" value="Search" onClick={handleClick} />
<br />
{startFetching && <SearchResults keyword={searchTerm} />}
</>
);
};
export default Home;
Basically, create a separate component that handles fetching and displaying UI based on the fetched content. And, create a state in the parent that tells when to render that component (and start fetching).
Credits: https://github.com/vercel/swr/issues/254

How do I retrieve an item data by id and pass it to another component in React16 with Hooks

I want to create an edit screen. I have a component called Task that looks like this
const Task = ({task}) => {
return (
<li>
<div>
<div>{task.text}</div>
{task.day}
</div>
<div className="icons">
<Link
to={`/edit/${task.id}`} >
<RiEdit2FillIcon />
</Link>
</div>
</li>
)
}
That goes to a parent component with a tasks.map() and then to the main component that will render the list of tasks. But from this component, I want to click on that Edit Icon and open an Edit screen that is already Routed like this <Route path='/edit/:id' component={EditTask}/> That EditTask component is what I am working on now
import React from 'react'
import {useState, useEffect} from 'react'
import { Link } from 'react-router-dom'
import Task from './components/Task'
const EditTask = () => {
const api ="http://localhost:5000"
const [tasks, setTasks] = useState([])
const [task, setTask] = useState([])
const [text, setText] = useState('')
const [day, setDay] = useState('')
const [reminder, setReminder] = useState(false)
const onSubmit = (e) => {
e.preventDefault()
updateData()
}
//Get Request
useEffect(() => {
const getTask = async () => {
const tasksFromServer = await fetchTask()
setTasks(tasksFromServer)
}
getTask()
},[])
const fetchTask = async (id) => {
const res = await fetch(`${api}/tasks/${id}`)
const data = await res.json()
console.log(data)
return data
}
//Update request
const updateData = async (id) => {
const taskToEdit = await fetchTask(id)
const updateTask = {
...taskToEdit,
reminder: !taskToEdit.reminder,
text: taskToEdit.text,
day: taskToEdit.day
}
const res = await fetch(`${api}/tasks/${id}`, {
method: 'PUT',
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(updateTask)
})
const data = await res.json()
setTasks(
tasks.map((task) =>
task.id === id
? {
...task,
reminder: data.reminder,
text: data.text,
day: data.day
}
: task
)
)
}
return (
<div>
<header className='header'>
<h1>Edit</h1>
<Link to="/" className="btn btn-primary">Go Back</Link>
</header>
<form className="add-form" onSubmit={onSubmit}>
<Task task={task}/>
<div className="form-control">
<label>Task</label>
<input type="text" placeholder="Add Task" value={text} onChange={(e)=> setText(e.target.value)} />
</div>
<div className="form-control">
<label>Day & Time</label>
<input type="text" placeholder="Add Day & Time" value={day} onChange={(e)=> setDay(e.target.value)}/>
</div>
<div className="form-control form-control-check">
<label>Set Reminder</label>
<input type="checkbox" checked={reminder} value={reminder} onChange={(e)=> setReminder(e.currentTarget.checked)}/>
</div>
<input className="btn btn-block" type="submit" value="Save Task" />
</form>
</div>
)
}
export default EditTask
I'm a bit lost here. I can't figure out how to pass the ID from Task.js to EditTask.js and populate the form with the data form that ID.
Thanks in advance
You can get id in EditTask with useParams in "react-router
import { useParams } from "react-router";
const EditTask = () => {
const { id } = useParams();
}

Resources