How could I achieve the state update effect in child components immediately? - reactjs

This is just something I am playing with. Idea was to be able to import JSON data (users) from a remote location, display one user on each click of the button and also be able to delete a user with the delete button.
I have successfully imported JSON into my console and then have been able to show it onto the browser, been able to add users with a click of the button but, I am currently struggling with being able o delete users.
Along with the above, I also had a couple of side questions:
1)- Does creating multiple states affect negatively on the codes' performance in any ways?
2)- Could I have avoided creating count state? I tried with a local variable but that ends up loading from initial value each time the states change. I am sure there must be many ways. Recursion may be one of them which I have just thought of while typing my question and might see how to apply it.
3)- I ended up using id for val in my Blog component because I was failing to be able to access key attribute via event object in handleDel function. How could have I accessed key attribute and if there is any other better approach?
4)- In my count and counter states I tried different initial values but only the below values made the desired effect possible. Why is it so please?
Thanks in advance
import useFetch from './useFetch';
const Blog = ({counter, del}) => {
const {user} = useFetch();
return (
<div>
{
counter.map(val => (
<div key = {val} id={val}>
{user && <p>First name is : {user[val].firstName}</p>}
{user && <p>Last Name is : {user[val].lastName}</p>}
{user && <p>Age is : {user[val].age}</p>}
<button onClick = {del}>Delete</button>
</div>
))
}
</div>
)
}
export default Blog;
import Blog from './Blog';
import { useState } from "react";
const Home = () => {
const [count, setCount] = useState(1);
const [counter, setCounter] = useState([0])
const handleClick = () => {
setCount( count + 1 );
setCounter([...counter,...[count]]);
}
const handleDel = (e) => {
counter.splice(e.target.parentElement.id,1);
}
return (
<div>
hello from home
<button onClick = {handleClick}>Click for us to import a blog</button>
<Blog prp = {{count, counter}} del = {handleDel} />
</div>
)
}
export { Home };

Related

React with Firestore autocomplete

Using ReactJS, Firestore - Firebase v9.
I have an autocomplete search bar on my web, built with just pure React. The question that I am trying to solve for at least one month is, if I can make this autocomplete input work with Firestore - (user type E.g 'elepha', auto suggestion appears with offer with word elephant - this is what I coded, and with same case, user will click on the suggestion of elephant, and this word elephant will be send to Firestore.)
Cuz there is not any solution on internet, I wonder, if my question is even possible to make, or not.
My simple autocomplete bar code - (animals_data stands for file with animals names)
and I tried to add onClick={pleaseSend} which is basic addDoc function, but when I click on the suggestion, in Firestore will only appear blank input "".
<SearchBar data={animals_data} />
And the filtering code:
function SearchBar({ placeholder, data }) {
const [filteredData, setFilteredData] = useState([]);
const [wordEntered, setWordEntered] = useState("");
const [newAnswer, setAnswer] = useState("")
const [users, setUsers] = useState([]);
const usersCollectionRef = collection(db, "Answers")
const createUser = async () => {
await addDoc(usersCollectionRef, {name: newAnswer}).then(()=>{
window.location.reload()
}).catch((err)=>{
console.log(err)
})
};
const handleFilter = (event) => {
const searchWord = event.target.value;
setWordEntered(searchWord);
const newFilter = data.filter((value) => {
return value.full_name.toLowerCase().includes(searchWord.toLowerCase());
});
if (searchWord === "") {
setFilteredData([]);
} else {
setFilteredData(newFilter);
}
};
const clearInput = () => {
setFilteredData([]);
setWordEntered("");
};
return (
<div className="search">
<div className="searchInputs">
<input
type="text"
placeholder={placeholder}
value={wordEntered}
onChange={handleFilter}
/>
</div>
{filteredData.length !== 0 && (
<div className="dataResult">
{filteredData.slice(0, 15).map((value, key) => {
return (
<a className="dataItem" onClick={createUser} target="_blank">
<p>{value.full_name} </p>
</a>
);
})}
</div>
)}
</div>
);
}
export default SearchBar;
EDIT 1: added code screenshots
web:
Thank you very much for reading.
after analyzing your code, I notice that you don't update the value newAnswer using its setter. Therefore, you should use the setter to update the state on user click and then add the firestorm document. You can do that by either using a button/unmodifiable input field in instead of an anchor tag to store the value of each option, then use this value inside the click handler to update the state and then use a useEffect to update firestore every time the state changes. Let me know if you need help with some code. Please post it below your original question as an edit without changing it.

How to render component as soon as the value stored in local in React

I want to show one component only if a value ('id') is stored in the localStorage. And id is stored when the button is clicked.
{localStorage.getItem('id') != null ? <Game/> : null }
It works fine, so I click the button and store the id in localStorage but only after I refresh I am able to see component. But I want to render the component as soon as it is stored in local (refresh the page shouldnt be needed)
Any suggest to do it?
My version, with custom hook.
import "./styles.css";
import { useState,useEffect } from "react";
function useLocalStorage(key) {
const [state, setState] = useState(localStorage.getItem(key));
function setStorage(item) {
localStorage.setItem(key, item);
setState(item);
}
return [state, setStorage];
}
export default function App() {
const [input, setInput] = useState("");
const [item, setItem] = useLocalStorage("myKey");
return (
<div className="App">
<input value={input} onInput={(e) => setInput(e.target.value)} />
<button onClick={() => setItem(input)}>Click</button>
<p>{item}</p>
</div>
);
}
https://codesandbox.io/s/bitter-sea-d6ni42?file=/src/App.js
components usually render with the state change. you can bind one state for local storage (if u need local storage anyway) and just update the state each time.
if you want to store in local storage then take value from the state.
example:
const [userid, setUserId] = useState(localStorage.getItem('id'));
const onSave : (value) => {
localStorage.setItem('id', JSON.stringify(value))
setUserId(value)
}
return (
<div>
{userid != null ? <Game/> : null }
</div>
)
You have to use state management, the local storage has updated but your DOM is unaware of it, to fix this check the following example
for this we use the useState hook, here we initialize the idLocal value with the value from local storage
let [idLocal, setIdLocal] = useState(localStorage.getItem("id"));
then you have to use this value for the condition as changes to this value will be visible in DOM , the setIdLocal is used to set or update this idLocal
{idLocal? <Game/> : null }
for updating the value on the button click
<button onClick={()=>setLocalstore()}>Off</button>
setLocalstore =()=>{
setIdLocal(whatever value you want)
}
check out this minimal example https://codesandbox.io/s/react-hooks-usestate-forked-5j6ck0?file=/src/index.js:567-605
1.Initiate state:
const [id, setId] = useState(
localStorage.getItem('id') ? <Game/> : null
);
2.Save every change inside local storage:
useEffect(() => {
localStorage.setItem('id', id);
}, [id])
3.Render component using state

Like Button with Local Storage in ReactJS

I developed a Simple React Application that read an external API and now I'm trying to develop a Like Button from each item. I read a lot about localStorage and persistence, but I don't know where I'm doing wrong. Could someone help me?
1-First, the component where I put item as props. This item bring me the name of each character
<LikeButtonTest items={item.name} />
2-Then, inside component:
import React, { useState, useEffect } from 'react';
import './style.css';
const LikeButtonTest = ({items}) => {
const [isLike, setIsLike] = useState(
JSON.parse(localStorage.getItem('data', items))
);
useEffect(() => {
localStorage.setItem('data', JSON.stringify(items));
}, [isLike]);
const toggleLike = () => {
setIsLike(!isLike);
}
return(
<div>
<button
onClick={toggleLike}
className={"bt-like like-button " + (isLike ? "liked" : "")
}>
</button>
</div>
);
};
export default LikeButtonTest;
My thoughts are:
First, I receive 'items' as props
Then, I create a localStorage called 'data' and set in a variable 'isLike'
So, I make a button where I add a class that checks if is liked or not and I created a toggle that changes the state
The problem is: I need to store the names in an array after click. For now, my app is generating this:
App item view
localStorage with name of character
You're approach is almost there. The ideal case here is to define your like function in the parent component of the like button and pass the function to the button. See the example below.
const ITEMS = ['item1', 'item2']
const WrapperComponent = () => {
const likes = JSON.parse(localStorage.getItem('likes'))
const handleLike = item => {
// you have the item name here, do whatever you want with it.
const existingLikes = likes
localStorage.setItem('likes', JSON.stringify(existingLikes.push(item)))
}
return (<>
{ITEMS.map(item => <ItemComponent item={item} onLike={handleLike} liked={likes.includes(item)} />)}
</>)
}
const ItemComponent = ({ item, onLike, liked }) => {
return (
<button
onClick={() => onLike(item)}
className={liked ? 'liked' : 'not-liked'}
}>
{item}
</button>
)
}
Hope that helps!
note: not tested, but pretty standard stuff

Data from useRef renders only after changing the code and saving it

I'm having an issue with my react app. I retrieve data from my elasticsearch server and trying to display it on the website.
const RecipesPage = (props: Props) => {
const recipes = useRef<Recipe[]>([]);
const avCategories = ['meats', 'pastas', 'vegan', 'seafood', 'desserts', 'all'];
const currentCategory = props.match.params.category_name.toLowerCase();
useEffect(() => {
const recipesReq = getRecipesByCategory(currentCategory);
recipesReq
.then((data) => recipes.current = data.hits.hits)
}, [currentCategory])
if (avCategories.includes(currentCategory)) {
return (
<div>
<Navbar />
<ul style={{marginTop: "5.5rem"}}>{recipes.current.map((recipe: Recipe) => <p key={recipe._id}>{recipe._source.recipe_name}</p>)}</ul>
</div>
);
} else {
return (
<div>
<Navbar />
<p style={{marginTop: "5.5rem"}}>No category named {currentCategory}</p>
</div>
);
}
};
export default RecipesPage
The problem is that when I'm trying to display the data it shows up only after saving the code and then after refreshing the page it's gone. I guess it's a problem related to useRef hook, but I'm not sure.
You should use state if you need the component to rerender.
When using useEffect, you shouldn't pass an array or object reference as a dependency. React uses referential comparison to check for changes, which means the useEffect hook will run every time a new object/array is created regardless if the actual data changes, which can cause an infinite render loop:
https://www.benmvp.com/blog/object-array-dependencies-react-useEffect-hook/

React custom hook with state variable as parameter

I have a react function component which sets an array of ids based on an user event on a link click(which opens a popup with some options that can be selected and has a callback once it is closed which will return the id of the selected element). these ids are passed to a child component which has a custom hook which uses these ids to perform some action. whenever i click on the link and select an element and close the popup.. get the error
"VM10715 react_devtools_backend.js:2430 You have changed a parameter while calling a hook which is supposed to remain unchanged [Array(2)]
0: (2) ["", "asdsadsad"]
lastIndex: (...)
lastItem: (...)
length: 1"
is there a way to make this work without running into this error? please see the code sample below
const TestComp = () => {
const [newIds, setNewIds] = useState([]);
const onPopupElementSelect = (ids) => {
setNewIds([...newIds, ids]);
};
return (
//renders some components
<>
<ImageComponent images={images} ids={newIds} onClick={handleClick} />
<Popup onSelect={onPopupElementSelect} />
</>
);
};
const ImageComponent = (props) => {
const { newIds, images } = props;
const newImages = useImages(ids || ['']); //customhook that fetches image details by ids
const imgs = images.map((i) => (
<div key={i.imageId}>
<img src={i.imageUrl} alt="" />
<Link onClick={handleClick} /> //opens the popup for user to select a new
image
</div>
));
return <div>{imgs}</div>;
};
ps: the paramerter names are not the issue.. this code is just a sample to give the basic idea of what i'm trying to do.
I think it is because you gave the same name to parameter and the state may be try newID as the parameter name
const onPopupElementSelect = (newId) => {
setIds(oldIds => [...oldIds, newId]);
};

Resources