hide and show names using react hooks - javascript

I am new to react hooks,
in my app initially, all the names of the users should be hidden.
later when I click each user it should show the name.
so I used the show and setshow.
when I tried to print the values in the browser, I don't see it.return(<div>{show}users Data{setShow}
I wrote click for each user but I am not sure how to hide and show.
there will be millions of users in my app, so whats the best way to hide and show the name when I click each
providing my code snippet and sandbox below
https://stackblitz.com/edit/react-t1mdfj?file=index.js
import React, { Component, useState } from "react";
import { render } from "react-dom";
import Hello from "./Hello";
import "./style.css";
function DataClick(){
const [show, setShow]= useState(false);
function showItem(e){
console.log("e--->", e.target);
setShow(true);
}
return(<div>{show}users Data{setShow}
<div onClick= {showItem}
//onClick={()=>setShow(true)}
>user1</div>
<div>John</div>
<div onClick= {showItem}
//onClick={()=>setShow(true)}
>user2</div>
<div>Mike</div>
<div onClick= {showItem}
//onClick={()=>setShow(true)}
>user3</div>
<div>Mike3</div><div onClick= {showItem}
//onClick={()=>setShow(true)}
>user4</div>
<div>Mik4e</div><div onClick= {showItem}
//onClick={()=>setShow(true)}
>user5</div>
<div>Mike5</div><div onClick= {showItem}
//onClick={()=>setShow(true)}
>user6</div>
<div>Mike6</div><div onClick= {showItem}
//onClick={()=>setShow(true)}
>user7</div>
<div>Mike7</div><div onClick= {showItem}
//onClick={()=>setShow(true)}
>user8</div>
<div>Mike8</div>
</div>);
}
render(<DataClick />, document.getElementById("root"));

#computer cool, Please see the below implementation to show and hide the usernames when user id is clicked, this is an updated code of #JMadelaine's implementation. Hope this will help. (In #JMadelaine's implementation one drawback I found is, once the username is shown it was not hiding back when clicking on the user id again because he was always setting the state to true onClick), please see the below code.
import React, { Component, useState } from "react";
const UserItem = ({ user }) => {
const [isNameShown, setIsNameShown] = useState(false)
const handleChange = () => {
setIsNameShown(prevState => !prevState)
}
return (
<div onClick={handleChange} >
<div>{user.id}</div>
{isNameShown && <div>{user.name}</div>}
</div>
)
}
function DataClick() {
const users = [
{
id: 'user1',
name: 'John',
},
{
id: 'user2',
name: 'Peter',
},
]
return (
<div>
{users.map(user => <UserItem user={user} />)}
</div>
)
}
export default DataClick;
Here the handleChange function sets the value of the state using the previous value by passing a callback function instead of direct value, so this callback function will get the previous state as argument and returns the inverted value, so if the user is opened, it will close, if the user is closed it will open.
EDIT:
Below is the explanation of the code setIsNameShown(prevState => !prevState)
setIsNameShown or any function that is returned by the useState hook can be written in the below ways.
1st way example:
setIsNameShown(false)
in this, you are directly passing the value to be set irrespective of the previous value.
2nd way example:
setIsNameShown((prevStateVariable) => {
return !prevStateVariable
})
or more concisely this same can be written as
setIsNameShown(prevStateVariable => !prevStateVariable)
in this case, the function setIsNameShown accepts the callback function which receives the previous state (to which the function is related to) as an argument.
So in cases where you need to set values to state which depends on the previous state value, use the callback function instead of directly providing the value.

The useState hook is just like a class component's state and setState. In your case show is the state variable that has a true or false value, and you can change this value using the function setShow.
To conditionally show the users' names, you should use the value of show like so:
return(
<div>
<div onClick={() => setShow(true)}>user1</div>
{show && <div>John</div>}
<div onClick={() => setShow(true)}>user2</div>
{show && <div>Mike</div>}
</div>
)
However, I don't think that is what you want. It sounds like you want to show only the name of the user that was clicked, and in the current state, when you click one user, all usernames will show because they all depend on the same state variable show.
You should create a separate component that contains the logic to show and hide a username, and map each user to that component. That way, each user has their own show state.
EDIT:
I've updated your code with the below:
// each user gets mapped to this component
const UserItem = ({user}) => {
// that means each user has their own 'isNameShown' variable
const [isNameShown, setIsNameShown] = useState(false)
return (
// if you click this div, this user's isNameShown value will be set to true
<div onClick={() => setIsNameShown(true)}>
// user.id is the id of the user from props
<div>{user.id}</div>
// only show this user's name if this user's isNameShown is true
{isNameShown && <div>{user.name}</div>}
</div>
)
}
function DataClick(){
// we just create some example users here for testing
const users = [
{
id: 'user1',
name: 'John',
},
{
id: 'user2',
name: 'Peter',
},
]
return(
<div>
// map each user to a UserItem, passing the user as a prop
{users.map(user => <UserItem user={user} />)}
</div>
)
}
I don't know what your users data structure looks like, so you should change it accordingly.
Something to learn here is that, if you find yourself repeating code over and over or copy pasting things again and again, then there is usually a better way of achieving what you want. Your list of users was basically copied and pasted over and over again, so you should create a single UserItem component instead.
There may be on issue with this code, which is that once a username is visible, you cannot hide it again, but I'll let you figure out how to do that if that is your intention.

Related

Implement Edit feature in a Note Application using react

I am trying to create a notes application wherein each note object contains a title and content. The user can add, deleted and update a note.
What I was able to achieve so far:
I am able to create a new note , push it into an array and also delete a note from the array . I am finding it a bit hard to edit an existing note.
This is how I want to implement the edit feature:
When the user clicks on the note, the data has to automatically fill into the input box, and the user can modify the data which is then saved into an object and pushed inside an array and then displayed onto the respective note.
When the user clicks on the Edit button, the note id is sent to the App component, the note is searched within the notes array and an object returned to the Create Area component. This object is then displayed on the input field. I'm using UseEffect() hook to display the object data on the input box, but I'm not able to edit the contents on the input box. Here's my code below:
App.jsx:
If the user clicked the edit button, it sets the IsDone state to true in the Edit function. The edit function gets an object from the Notes component
import Header from "./Header";
import CreateArea from "./CreateArea";
import Note from "./Note";
import Footer from "./Footer";
import { useState } from "react";
function App() {
const [noteArray, setArray] = useState([]);
const [isDone,setDone] = useState(false);
const [editNote,setEditNote] = useState({
title:"",
content:""});
function AddOnClick(note) {
setArray((prevNote) => {
return [...prevNote, note];
});
}
function DeleteOnClick(id) {
setArray((prevNote) => {
return prevNote.filter((note, index) => {
return index !== id;
});
});
}
function EditNote(obj)
{
setDone(true);
setEditNote(prevState=>{
return { ...prevState,...obj}});
}
return (
<div>
<Header />
<CreateArea AddOnClick={AddOnClick} noteEdit = {editNote} editFunction = {EditNote}btnClicked = {isDone}/>
{noteArray.map((note, index) => (
<Note
key={index}
id={index}
title={note.title}
content={note.content}
deleteNote={DeleteOnClick}
EditNote = {EditNote}
/>
))}
<Footer />
</div>
);
}
export default App;
Notes.jsx: The id of the note is also included in the object that's passed to App component through the EditNote() function
function Note(props) {
const obj = {title : props.title,
content: props.content,
id:props.id}
return (
<div className="note">
<h1>{props.title}</h1>
<p>{props.content}</p>
<button
onClick={() => {
props.deleteNote(props.id);
}}
>
DELETE
</button>
<button onClick={()=>{props.EditNote(obj)}}>EDIT</button>
</div>
);
}
export default Note;
CreateArea: If the buttonClicked value is true, I'm calling the handleEdit() that takes the object sent from the EditNote() in App.jsx to saves it to to note object using useState() which automatically updates the input and text area field using event.target.value with the help of useEffect().
import { useState } from "react";
function CreateArea(props) {
const [note, setNote] = useState({
title: "",
content: ""
});
function handleChange(event) {
console.log(event.target);
const { name, value } = event.target;
setNote((prevNote) => {
return { ...prevNote, [name]: value };
});
}
function addNote(event) {
setNote({ title: "", content: "" });
props.AddOnClick(note,note.id);
event.preventDefault();
}
function handleEdit()
{
setNote(prevValue=>{
return {...prevValue,...props.noteEdit}
})
}
useEffect (()=>{
if(props.btnClicked){handleEdit();
}
});
return (
<div>
<form>
<input name="title" id="title" value={note.title}onChange={handleChange}placeholder="Title"/>
<textarea name="content" id="content" value={note.content}onChange={handleChange} placeholder="Take a note..." rows="3"/>
<button onClick={addNote}>Add</button>
</form>
</div>
);
}
export default CreateArea;
The code runs well but now I can't add any more text on the input box,it just blocks me from doing it.I tried calling HandleChange() inside UseEffect(), that throws an error saying: Cannot read properties of target:undefined at HandleChange() I really need help how to implement edit.
I tried directly populating the input box and the text area field using document.getElementById.value = myValue even that does not seem to work

React js doenst re-render after updating state

i created a parent component that gathers a bunch of child components:
Expenses - Parent,
ExpenseItem - child.
i defined the first value of ExpenseItem by hard coding inside of Expenses
i entered the dynamic value to the ExpenseItem component element and then used "props" parameter to get the data from ExpenseItem to Expenses.
function Expenses() {
const Data = [{
title: `מסרק`,
date: Date(),
amount: 2.95,
},
]
return (
<div>
<ExpenseItem
title={Data[0].title}
date={Data[0].date}
amount={Data[0].amount}
/>
</div>
);
}
now, i want to update the values through a button "edit" in ExpenseItem Component.
when i do update values through useState and console log them i see the updated values,
but, the component doesnt re-renders so i see the prev value on the screen. though if i try to hard code the value it does re-renders and changes the value on the screen.
function ExpenseItem(props) {
const [title, setTitle] = useState(props.title);
const [date, setDate] = useState(props.date);
const [amount, setAmount] = useState(props.amount);
const clickHandle = () => {
console.log(title);
setTitle("חתול")
console.log(Date());
setDate(date)
console.log(amount);
setAmount("222")
}
return (
<div className="ExpenseItem">
<div className="ExpenseItem_EditFunctions">
<p className="ExpenseItemDate">{props.date}</p>
<div className="ExpenseItem_EditFunctions_Icons">
<span className="material-icons delete">delete</span>
<span className="material-icons edit" onClick={clickHandle}>
edit
</span>
</div>
<div className="ExpenseItem_MainContent">
<h3 className="ExpenseItemTitle">{props.title}</h3>
<p className="ExpenseItemAmount">₪{props.amount}</p>
</div>
</div>
</div>
);
}
It's because you are using props to display values you need to display state values instead of them.You need to replace all the prop values with corresponding state values to make this code work properly. Just replace {props.date} to {date} {props.title} to {title} and {props.amount} to {amount}

How to save a component state after re-rendering? React js

There are some movie cards that clients can click on them and their color changes to gray with a blur effect, meaning that the movie is selected.
At the same time, the movie id is transferred to an array list. In the search bar, you can search for your favorite movie but the thing is after you type something in the input area the movie cards that were gray loses their style (I suppose because they are deleted and rendered again based on my code) but the array part works well and they are still in the array list.
How can I preserve their style?
Search Page:
export default function Index(data) {
const info = data.data.body.result;
const [selectedList, setSelectedList] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
return (
<>
<main className={parentstyle.main_container}>
<NavBar />
<div className={style.searchbar_container}>
<CustomSearch
onChange={(e) => {
setSearchTerm(e.target.value);
}}
/>
</div>
<div className={style.card_container}>
{info
.filter((value) => {
if (searchTerm === '') {
return value;
} else if (
value.name
.toLocaleLowerCase()
.includes(searchTerm.toLocaleLowerCase())
) {
return value;
}
})
.map((value, key) => {
return (
<MovieCard
movieName={value.name}
key={key}
movieId={value._id}
selected={selectedList}
setSelected={setSelectedList}
isSelected={false}
/>
);
})}
</div>
<div>
<h3 className={style.test}>{selectedList}</h3>
</div>
</main>
Movie Cards Component:
export default function Index({ selected, movieName, movieId, setSelected }) {
const [isActive, setActive] = useState(false);
const toggleClass = () => {
setActive(!isActive);
};
useEffect(()=>{
})
const pushToSelected = (e) => {
if (selected.includes(e.target.id)) {
selected.splice(selected.indexOf(e.target.id), 1);
console.log(selected);
} else {
selected.push(e.target.id);
console.log(selected);
console.log(e.target);
}
setSelected([...selected]);
toggleClass();
};
return (
<div>
<img
className={isActive ? style.movie_selected : style.movie}
id={movieId}
name={movieName}
src={`images/movies/${movieName}.jpg`}
alt={movieName}
onClick={pushToSelected}
/>
<h3 className={style.title}>{movieName}</h3>
</div>
);
}
I can't directly test your code so I will assume that this is the issue:
Don't directly transform a state (splice/push) - always create a clone or something.
Make the setActive based on the list and not dependent. (this is the real issue why the style gets removed)
try this:
const pushToSelected = (e) => {
if (selected.includes(e.target.id)) {
// filter out the id
setSelected(selected.filter(s => s !== e.target.id));
return;
}
// add the id
setSelected([...selected, e.target.id]);
};
// you may use useMemo here. up to you.
const isActive = selected.includes(movieId);
return (
<div>
<img
className={isActive ? style.movie_selected : style.movie}
id={movieId}
name={movieName}
src={`images/movies/${movieName}.jpg`}
alt={movieName}
onClick={pushToSelected}
/>
<h3 className={style.title}>{movieName}</h3>
</div>
);
This is a very broad topic. The best thing you can do is look up "React state management".
As with everything in the react ecosystem it can be handled by various different libraries.
But as of the latest versions of React, you can first start by checking out the built-in tools:
Check out the state lifecycle: https://reactjs.org/docs/state-and-lifecycle.html
(I see in your example you are using useState hooks, but I am adding these for more structured explanation for whoever needs it)
Then you might want to look at state-related hooks such as useState: https://reactjs.org/docs/hooks-state.html
useEffect (to go with useState):
https://reactjs.org/docs/hooks-effect.html
And useContext:
https://reactjs.org/docs/hooks-reference.html#usecontext
And for things outside of the built-in toolset, there are many popular state management libraries that also work with React with the most popular being: Redux, React-query, Mobx, Recoil, Flux, Hook-state. Please keep in mind that what you should use is dependant on your use case and needs. These can also help you out to persist your state not only between re-renders but also between refreshes of your app. More and more libraries pop up every day.
This is an ok article with a bit more info:
https://dev.to/workshub/state-management-battle-in-react-2021-hooks-redux-and-recoil-2am0#:~:text=State%20management%20is%20simply%20a,you%20can%20read%20and%20write.&text=When%20a%20user%20performs%20an,occur%20in%20the%20component's%20state.

UseState not saving user input [duplicate]

This question already has answers here:
Push method in React Hooks (useState)?
(8 answers)
Closed 2 years ago.
Im working on a todo aplication in react using useState, im trying to save user input and then after they click submit push it to the listArray, later to display it...
I think im doing something wrong in the updateArray function, but I can seem to understand what.
import React, { useState } from "react";
function App() {
const listArray = [""];
const [list, updateList] = useState("");
function handleChange(event) {
const { name, value } = event.target;
updateList(value);
//console.log(list);
}
function updateArray() {
console.log(list);
listArray.push(list);
console.log(listArray);
}
return (
<div className="container">
<div className="heading">
<h1>To-Do List</h1>
</div>
<div className="form">
<input name="entry" onChange={handleChange} type="text" />
<button>
<span onSubmit={updateArray}>Add</span>
</button>
</div>
<div>
<ul>
<li>{listArray[0]}</li>
</ul>
</div>
</div>
);
}
export default App;
There are several issues with your current code and I will briefly describe and provide a solution to fix them.
Your functions are working fine and as expected, but in a React application there are few ways to re-render a page or component and changing the local variable is not one of them. So you need to use a local state instead of local listArray variable. Since there is one state already you should either define another state or make your current state an object and put your component related states into it in one place I will go with the second approach, because it's more elegant and cleaner one.
const [state, setState] = useState({ list: [], input: "" });
After you define your state, you need to change them properly without effecting unnecessary ones. You just need to send the previous state, save it in the new one and only change the related state in each function. So with ES6 syntax, updating input state will look like this:
setState((prev) => ({ ...prev, input: value })); // immediate return "({})" of an object with iterating through the previous values "...prev" and updating input "input: value"
NOTE: You can read more about spread operator (...) here.
So your handle and updateArray function will look like this:
function handleChange(event) {
const { value } = event.target;
setState((prev) => ({ ...prev, input: value }));
}
function updateArray() {
setState((prev) => ({ ...prev, list: [...state.list, state.input] }));
}
onSubmit event will only work on forms or submit buttons so you need to change it to onClick. Also, to make the whole button catch the onclick action you need to set it on the button itself, instead of span element.
<button onClick={updateArray}> <!-- It's better to assign onclick action to an element with a function or arrow function to prevent it from running in the first render "(event) => updateArray(event)" -->
<span>Add</span>
</button>
And finally you need to map through the updated array of todo items in your JSX.
<ul>
{state.list.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
Working Demo:
Save the current value into the state, and keep the list as well into the state so that it isn't cleared each render cycle.
import React, { useState } from "react";
function App() {
const [list, updateList] = useState([]);
const [currentValue, setCurrentValue] = useState()
function handleChange(event) {
setCurrentValue(event.target.value)
}
function handleClick() {
updateList([...list, currentValue])
}
return (
<div className="container">
<div className="heading">
<h1>To-Do List</h1>
</div>
<div className="form">
<input name="entry" onChange={handleChange} type="text" />
<button type="button" onClick={handleClick}>
<span>Add</span>
</button>
</div>
<div>
<ul>
{list.map((res) => (
<li key={res}>{res}</li>
))}
</ul>
</div>
</div>
);
}
export default App;
Also, moving the onClick to the button makes more sense semantically (and even for the UX) but it's up to you.
listArray is cleared with every rerender.
You should store your data in state. So
const [listArray, setListArray] = useState([])
And updateArray should look like:
function updateArray() {
setListArray([...listArray, list]);
}
I guess, in updateArray function should be some logic to clear list
Notice how listArray will always go back to the default value when your app component re-renders (using useState may re-render the component)
I would instead make the string the user inputs a normal const and use useState for the array so the values in the array will be saved across re-renders

Handle React re-rendering

I have a similar situation like the one in the sandbox.
https://codesandbox.io/s/react-typescript-fs0em
Basically what I want to achieve is that Table.tsx is my base component and App component is acting like a wrapper component. I am returning the JSX from the wrapper file.
Everything is fine but the problem is whenever I hover over any name, getData() is called and that is too much rerendering. Here it is a simple example but in my case, in real, the records are more.
Basically Table is a generic component which can be used by any other component and the data to be displayed in can vary. For e.g. rn App is returning name and image. Some other component can use the Table.tsx component to display name, email, and address. Think of App component as a wrapper.
How can I avoid this getData() to not to be called again and again on hover?
Can I use useMemo or what approach should I use to avoid this?
Please help
Every time you update the "hover" index state in Table.jsx it rerenders, i.e. the entire table it mapped again. This also is regenerating the table row JSX each time, thus why you see the log "getData called!" so much.
You've effectively created your own list renderer, and getData is your "renderRow" function. IMO Table shouldn't have any state and the component being rendered via getData should handle its own hover state.
Create some "row component", i.e. the thing you want to render per element in the data array, that handles it's own hover state, so when updating state it only rerenders itself.
const RowComponent = ({ index, name }) => {
const [hov, setHov] = useState();
return (
<div
key={name}
onMouseEnter={() => setHov(index)}
onMouseLeave={() => setHov(undefined)}
style={{ display: "flex", justifyContent: "space-around" }}
>
<div> {name} </div>
<div>
<img
src={hov === index ? img2 : img1}
height="30px"
width="30px"
alt=""
/>
</div>
</div>
);
};
Table.jsx should now only take a data prop and a callback function to render a specific element, getData.
interface Props {
data: string[];
getData: () => JSX.Element;
}
export const Table: React.FC<Props> = ({ data, getData }) => {
return (
<div>
{data.map((name: string, index: number) => getData(name, index))}
</div>
);
};
App
function App() {
const data = ["Pete", "Peter", "John", "Micheal", "Moss", "Abi"];
const getData = (name: string, index: number, hov: number) => {
console.log("getData called!", index);
return <RowComponent name={name} index={index} />;
};
return <Table data={data} getData={getData} />;
}

Categories

Resources