How to implement search after data fetch with React Hooks - javascript

I have done a fetch from an API and I want to add a search component so I can be able to search a single user from the data fetched. And I need some help to be able to do it properly. Thanks for your help and I hope that I have been understandable. Here below you can see what I have done:
DataFethed.js:
import React, { useState, useEffect } from "react";
import axios from "axios";
import Search from "./Search";
function DataFethed() {
const [searchValue, setSearchValue] = useState("");
const [users, setUsers] = useState([]);
useEffect(() => {
axios
.get(
"https://gist.githubusercontent.com/benna100/5fd674171ea528d7cd1d504e9bb0ca6f/raw"
)
.then(res => {
console.log(res);
setUsers(res.data);
})
.catch(error => {
console.log(error);
});
}, []);
const searchHandler = value => {
setSearchValue(value);
};
let updateUsers = users.filter(item => {
return item.name.toLowerCase().includes(searchValue);
}, []);
return (
<div className="App">
<h5>Shift OverViewShift</h5>
<Search searchHandler={searchHandler} />
{updateUsers.map((shifts, index) => (
<DataFethed key={index} props={shifts} />
))}
<ul>
{users.map(user => (
<ol key={user.name}>
<br />
{user.name} <br />
{user.start} <br />
{user.end}
</ol>
))}
</ul>
</div>
);
}
export default DataFetched;
Search.js:
import React from "react";
const Search = ({ searchHandler }) => {
const handleSearchInputChange = e => {
searchHandler(e.target.value);
};
return (
<form className="search">
<input
onChange={handleSearchInputChange}
type="text"
placeholder="Search Name..."
/>
<i className="fas fa-search" type="submit" value="SEARCH"></i>
</form>
);
};
export default Search;

Due to the comments we exchanged, you just need a conditional rendering. Inside DataFetched, you should write this return statement:
return (
<div className="App">
<h5>Shift OverViewShift</h5>
<Search searchHandler={searchHandler} />
<ul>
{(searchValue === '' ? users : updateUsers).map(user => (
<ol key={user.name}>
<br />
{user.name} <br />
{user.start} <br />
{user.end}
</ol>
))}
</ul>
</div>
);
Basically, now, when searchValue is NOT empty (meaning you are searching), you use updateUsers to render the user. Instead, when searchValue is empty, you render ALL the user by using users.
Moreover, since you have the same structure in both users and updateUsers, you can use the same map() function: notice that the ternary operator only specifies if using users or updateUsers.

Related

How to modify react button "More"?

I have the following React component:
import React from "react";
import { useState, useEffect } from "react";
import { TailSpin } from "react-loader-spinner";
function Pokemon({ name, url }) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then((r) => r.json())
.then(setData);
}, [url]);
const onClickButtonChange = () => {
let cardMore = document.querySelector(".card_more");
let cardMain = document.querySelector(".card_main");
cardMore.style.display = "block";
cardMain.style.display = "none";
};
return (
<div>
{data ? (
<div>
<div className="card card_main">
<div className="animate__animated animate__bounceInUp">
<div className="card-image">
<img src={data.sprites.front_default} alt="pokemon_img" />
<span className="card-title">{name}</span>
<button onClick={onClickButtonChange}>More</button>
</div>
<div className="card-content">
{data.abilities.map((n, index) => (
<p key={index}>{n.ability.name}</p>
))}
</div>
</div>
</div>
<div className="card card_more">
<p>{data.height}</p>
<p>{data.weight}</p>
</div>
</div>
) : (
<div>
<TailSpin type="Puff" color="purple" height={100} width={100} />
</div>
)}
</div>
);
}
export { Pokemon };
My implementation of the More button needs to display additional features (the card_more block). Right now this function only works on the very first element. I understand that in React this can most likely be done more correctly, but I don’t know how, so I use CSS styles.
P.S Edited:
I tried to use React features, maybe someone can tell me or does it make sense?
import React from "react";
import { useState, useEffect } from "react";
import { TailSpin } from "react-loader-spinner";
function Pokemon({ name, url }) {
const [data, setData] = useState(null);
const [show, setShow] = useState(false);
useEffect(() => {
fetch(url)
.then((r) => r.json())
.then(setData);
}, [url]);
const handleMore = async () => {
if (show === true) {
setShow(false);
} else if (show === false || !data) {
const r = await fetch(url);
const newData = await r.json();
setData(newData);
setShow(true);
}
};
return (
<div>
{data && show ? (
<div>
<div className="card card_main">
<div className="animate__animated animate__bounceInUp">
<div className="card-image">
<img src={data.sprites.front_default} alt="pokemon_img" />
<span className="card-title">{name}</span>
</div>
<div className="card-content">
{data.abilities.map((n, index) => (
<p key={index}>{n.ability.name}</p>
))}
</div>
</div>
<button onClick={handleMore}>More</button>
</div>
<div className="card card_more">
<p>{data.height}</p>
<p>{data.weight}</p>
</div>
</div>
) : (
<div>
<TailSpin type="Puff" color="purple" height={100} width={100} />
</div>
)}
</div>
);
}
export { Pokemon };
Youre right, this isn't the way you should do it in React. But your problem in your onClickButtonChange-Function is that youre only getting one element with document.querySelector(".card_more") and everytime you call it you get the same element back (No matter on which card you call it)
What you need to do is: Identify the single component elements. Thats most likely solved by passing a id/key value down via props and then putting this id on a parent-element (e.g. div.card) and you give it an id:
<div className="card card_main" id={props.keyvalue}>
....
</div>
And then in your onClickButtonChange-Function you call:
let cardMore = document.querySelector(`#${props.keyvalue} .card_more`);
...
This should give you the right element.

trying to update a firebase document with update() but it not updating the document

i am trying to make a blog. I am working on an edit post functionality. When i call the ref.update() it says that the update worked but nothing is changed in the database in the following code.
Code
import AuthCheck from "../../components/AuthCheck";
import { firestore } from "../../lib/firebase";
import { useDocumentData } from "react-firebase-hooks/firestore";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { useForm } from "react-hook-form";
import toast from "react-hot-toast";
import ReactMarkdown from "react-markdown";
import Link from "next/link";
export default function Editposts({}) {
return (
<>
<AuthCheck>
<PostManager />
</AuthCheck>
</>
);
}
const PostManager = () => {
const [preview, setPreview] = useState(false);
const router = useRouter();
const { slug } = router.query;
const postRef = firestore.collection("blogs").doc(slug);
const [post] = useDocumentData(postRef);
return (
<>
<div className="container">
{post && (
<>
<section>
<h1>{post.title}</h1>
<h4>{post.slug}</h4>
<Postform
postRef={postRef}
defaultValues={post}
preview={preview}
/>
</section>
<aside>
<h3>Tools</h3>
<button onClick={() => setPreview(!preview)}>
{preview ? "Edit" : "Preview"}
</button>
<Link href={`/${post.slug}`}>
<button className="btn btn-secondary">Live view</button>
</Link>
{/* <DeletePostButton postRef={postRef} /> */}
</aside>
</>
)}
</div>
</>
);
};
const Postform = ({ postRef, defaultValues, preview }) => {
const { register, handleSubmit, reset, watch } = useForm({
defaultValues,
mode: "onChange",
});
const updatePost = async ({ content, published }) => {
await postRef.update({
content,
published,
});
reset({ content, published });
toast.success("Post updated successfully!");
};
return (
<form onSubmit={handleSubmit(updatePost)}>
{preview && (
<div className="card">
<ReactMarkdown>{watch("content")}</ReactMarkdown>
</div>
)}
<div className={preview ? `d-none`:``}>
<textarea
className='form-control'
name="content"
{...register("test", { required: true })}
></textarea>
<fieldset>
<input
className="form-check-input"
name="published"
type="checkbox"
{...register("test", { required: true })}
/>
<label>Published</label>
</fieldset>
<button type="submit" className="btn btn-secondary">
Save Changes
</button>
</div>
</form>
);
};
here when i submit the form, the popup says that the update was successful but when i cheeck the database, nothing changed. Here is an example image
Ok, so i find out the problem.
so the thing was i was following a tutorial and he was using 6.X.X which is the oudated version. So i went to the internet trying to find a solution and copy pasted a line of cide which is {...register("test", { required: true })} here i named the field value to 'test' instead of 'content' and that was causing the problem because there was no 'test' field to be updated. Hope someone finds this helpful

Update a React Element with the Data of Another Component's API Response

I am trying to make a simple react app that pulls info from a MySQL database ("username", "balance", "purchases").
So far, I've used node and react to pull from the database with an HTTP query and to display each element on the website.
I then created the API query for searching the database for all entries that start with what I've typed into the search bar.
The issue I'm running into is how do I change the state of the elements that display the username, etc with the new filtered information from the API query? The search bar and data elements are two separate components so I can't use the use effect hook natively.
I cant use the filter method because the database is huge and I've sent my query limit to 100.
Here's my code so far:
PlayerData.js
import axios from 'axios';
import React,{useState, useEffect} from 'react';
const Player = () => {
const [playerData,setPlayerData]=useState([])
useEffect(()=>{
axios.get("http://localhost:3001/api/get").then((res)=>{
console.log(res.data)
setPlayerData(res.data)
})
.catch(err=>{
console.log(err);
})
},[])
return (
<>
{playerData.map((data,id)=>{
return <div className="Player" key={id}>
<span className="Username"> { data.name } </span>
<span className="Crystals"> { data.balance } </span>
<span className="DateModi"> {Object.keys(JSON.parse(data.items)).length} </span>
</div>
})}
</>
)
};
export default Player;
SearchBar.js
import { useState } from "react";
import axios from 'axios'
const Search = () => {
const [searchTerm, setSearchTerm] = useState("")
axios.get(`http://localhost:3001/api/getSearchName/${searchTerm}`).then((res)=>{
console.log(res.data)
})
return (
<div className="Search">
<input className = "InputField" type="text" placeholder="Enter Username:" onChange={e => {setSearchTerm(e.target.value)}}/>
<span className="SearchButton" onClick={console.log(searchTerm)}>
Search
</span>
</div>
)
};
export default Search;
If I understood the question correctly, you need to set the state of PlayerData to a shared component(App), and pass it to the Player.js component. Then when searching it will be overwritten and update the information in the Player.js
function App() {
const [playerData, setPlayerData] = useState([]);
useEffect(() => {
fetchData();
}, []);
const fetchData = () =>
axios
.get("http://localhost:3001/api/get")
.then((res) => {
setPlayerData(res.data);
})
.catch((err) => {
console.log(err);
});
const handleSearch = (text) => {
const clearText = text.trim();
if (!clearText.length) {
fetchData();
return;
}
axios
.get(`http://localhost:3001/api/getSearchName/${clearText}`)
.then((res) => {
setPlayerData(res.data);
});
};
return (
<div>
<div>
<Search handleSearch={handleSearch} />
</div>
<div>
<Player playerData={playerData} />
</div>
</div>
);
}
Search.js
const Search = ({ handleSearch }) => {
const [searchTerm, setSearchTerm] = useState("");
return (
<div className="Search">
<input
className="InputField"
type="text"
placeholder="Enter Username:"
onChange={(e) => {
setSearchTerm(e.target.value);
}}
/>
<span className="SearchButton" onClick={() => handleSearch(searchTerm)}>
Search
</span>
</div>
);
};
Player.js
const Player = ({ playerData }) => {
return (
<>
{playerData?.length ? (
playerData.map((data, id) => {
return (
<div className="Player" key={id}>
<span className="Username"> {data.name} </span>
<span className="Crystals"> {data.balance} </span>
<span className="DateModi">
{" "}
{Object.keys(JSON.parse(data.items)).length}{" "}
</span>
</div>
);
})
) : (
<div>Loading...</div>
)}
</>
);
};

React: Fetch Data onSubmit, not on onChange

I got this code working pretty much how I want it. However, it's fetching & display data after each keystroke. I only want it to fetch once, when I hit submit.
Also, if there's anything i'm doing that's not "best practice" please let me know so I don't make silly mistakes in the future.
import React, { useEffect, useState } from "react";
export default function App() {
const [data, setData] = useState(null);
const [query, setQuery] = useState("");
useEffect(() => {
if (!query) return;
async function fetchData() {
const response = await fetch(
`https://www.omdbapi.com/?apikey=2e8b5857&s=${query}`
);
const data = await response.json();
const results = data.Search;
setData(results);
}
fetchData();
}, [query]);
const handleSubmit = (e) => {
e.preventDefault();
setQuery(query);
};
return (
<div
style={{
margin: 20,
}}
>
<form onSubmit={handleSubmit}>
<br />
<label>
Input Movie:{" "}
<input
type="text"
placeholder="ex. Harry Potter"
value={query}
onChange={(e) => {
setQuery(e.target.value);
}}
/>
</label>
<input type="submit" value="Submit" onClick={() => setQuery} />
</form>
{data &&
data.map((movie) => (
<div key={movie.imdbID}>
<h1>{movie.Title}</h1>
<h4>
{movie.Year} | {movie.imdbID}
</h4>
<img alt={movie.imdbID} src={`${movie.Poster}`} />
</div>
))}
</div>
);
}
Since you only want it after submit, you can skip the useEffect with [query] and just copy the same logic inside your handleSubmit like so :-
import React, { useEffect, useState } from "react";
export default function App() {
const [data, setData] = useState(null);
const [query, setQuery] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
if (!query) return;
async function fetchData() {
const response = await fetch(
`https://www.omdbapi.com/?apikey=2e8b5857&s=${query}`
);
const data = await response.json();
const results = data.Search;
setData(results);
}
fetchData();
};
return (
<div
style={{
margin: 20,
}}
>
<form onSubmit={handleSubmit}>
<br />
<label>
Input Movie:{" "}
<input
type="text"
placeholder="ex. Harry Potter"
value={query}
onChange={(e) => {
setQuery(e.target.value);
}}
/>
</label>
<input type="submit" value="Submit"/>
</form>
{data &&
data.map((movie) => (
<div key={movie.imdbID}>
<h1>{movie.Title}</h1>
<h4>
{movie.Year} | {movie.imdbID}
</h4>
<img alt={movie.imdbID} src={`${movie.Poster}`} />
</div>
))}
</div>
);
}
Here's the codesandbox :-
Pass the code that is inside the useEffect, that is, the fetch function, inside the submit function. leaving useEffect unused

Filter a list based on Input

const App = () => {
const [searchTerm, setSearchTerm] = React.useState('');
const stories = [
...
];
const handleSearch = event => {
setSearchTerm(event.target.value);
};
const searchStories = stories.filter((story) => {
return story.title.includes(searchTerm);
})
return (
<div>
<h1>My Hacker Stories</h1>
<Search onSearch={handleSearch}/>
<hr />
<List list={searchStories}/>
</div>
);
};
const Search = (props) =>{
return (
<div>
<label htmlFor="search"><strong>Search:</strong></label> { ' '}
<input id='search' type='text' onChange={props.onSearch}/>
</div>
);
};
const List = ({list}) =>
list.map((item) =>
(
<div key={item.objectID}>
<span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
<span>{item.num_comments}</span>
<span>{item.points}</span>
</div>
)
)
I am trying to filter the List Component based on the search input. It's working unless if I put a search term in the input box. When I try to search an item, List is empty, nothing is showing, it's empty List, however, there is no error showing.
I've change your code a little to produce a runnable snippet which you can change back to your code,
just need to add another state for searchStories and use useEffect for filtering when searchTerm changes like this:
*Click the Run Code Snippet and type h so you can see how filter works
const App = () => {
const [searchTerm, setSearchTerm] = React.useState("");
const [searchStories, setSearchStories] = React.useState([]);
const stories = ["hello", "hi", "bye", "have a good day"];
const handleSearch = event => {
setSearchTerm(event.target.value);
};
React.useEffect(() => {
setSearchStories(
stories.filter(story => {
return story.includes(searchTerm);
})
);
}, [searchTerm]);
return (
<div>
<h1>My Hacker Stories</h1>
<Search onSearch={handleSearch} />
<hr />
<List list={searchStories} />
</div>
);
};
const Search = props => {
return (
<div>
<label htmlFor="search">
<strong>Search:</strong>
</label>{" "}
<input id="search" type="text" onChange={props.onSearch} />
</div>
);
};
const List = ({ list }) =>
list.map(item => (
<div key={item}>
{item}
{/* <span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
<span>{item.num_comments}</span>
<span>{item.points}</span> */}
</div>
));
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
The filter function checks whether the searchTerm is present in our story item's title, but it's still too opinionated about the letter case. If we search for "react", there is no filtered "React" story in your rendered list. To fix this problem, we have to lower case the story's title and the searchTerm.
const App = () => {
...
const searchedStories = stories.filter(function(story) {
return story.title
.toLowerCase()
.includes(searchTerm.toLowerCase());
});
...
};

Categories

Resources