how to toggle with react hooks - reactjs

I have 2 buttons that can be toggled by that simple hook:
const [extendDetails, setExtendDetails] = useState(false);
const handleExtendDetails = () => setExtendDetails(!extendDetails);
const [extendPictures, setExtendPictures] = useState(false);
const handleExtendPictures = () => setExtendPictures(!extendPictures);
And these are the buttons:
<button onClick={handleExtendDetails}>Extend Details</button>
<button onClick={handleExtendPictures}>Extend Pictures</button>
Is there some sort of way to name the buttons and use e or some kind of a variable so I won't need to declare a hook for each button in case I've got 20 buttons and not just 2?

You can try using defining simple Object and target name combination.
const initialState = () => ({
extendDetails: false,
extendPictures: false
})
export default function App() {
const [toggle, setToggle] = useState(initialState())
const handleToggle = (e) => {
const { name } = e.target
setToggle({ ...toggle, [name]: !toggle[name] })
}
return (
<div>
<button name="extendDetails" onClick={handleToggle}>{toggle.extendDetails ? 'Open' : 'Close' } Extend Details</button>
<button name="extendPictures" onClick={handleToggle}>{toggle.extendPictures ? 'Open' : 'Close' } Extend Pictures</button>
</div>
);
}
Demo link is here

One option is to use an array instead:
const [toggledButtons, setToggledButtons] = useState(() => Array.from(
{ length: 20 },
() => false
));
Then you could do something like
const toggle = (i: number) => () => setToggledButtons(
toggledButtons.map((current, j) => i === j ? !current : current)
);
<button onClick={toggle(0)}>Extend Details</button>
<button onClick={toggle(1)}>Extend Pictures</button>

Related

react - text input give me null

(in REACT)
i have app function :
function App() {
const [welcomeMenu, setWelcomeMenu] = useState(true);
const [gameMenu, setGameMenu] = useState(false);
const [username, setUsername] = useState('');
const welcomeMenuShow = () => {
setWelcomeMenu(false);
}
const getUserName = (value) => {
setUsername(value);
console.log(username);
};
return (
<div className="App">
{
welcomeMenu ? <WelcomeMenu gameStarter={welcomeMenuShow} getUserName={getUserName}/> : null
}
</div>
);
}
in welcomemenu component i pass getUserName function to get username which user input
next in Welcome menu i have :
const WelcomeMenu = ({ gameStarter, getUserName }) => {
return (
<div className="welcome-menu">
<WelcomeText />
<WelcomeBoard gameStarter={gameStarter} getUserName={getUserName}/>
</div>
)
};
i pass get User Name in second time
in WelcomeBoard i have:
const WelcomeBoard = ({ gameStarter, getUserName }) => {
const [text, setText] = useState('');
const [warning, setWarning] = useState(false);
const checkBtn = (event) => {
if(text) {
gameStarter();
} else {
setWarning(true);
setTimeout(() => {
setWarning(false);
}, 3000);
}
};
const handleChange = (event) => {
setText(event.target.value);
};
return (
<div className="welcome-board">
<div className="username">Please enter the name</div>
<input type="text" value={text} onChange={handleChange} className="username-input" />
<button className="username-btn" onClick={() => {
getUserName(text);
checkBtn();
}}>start</button>
{warning ? <Warning /> : null}
</div>
)
};
in input onchange i make state and pass the input value on text state
next on button i have on click which active 2 function:
getUserName(text) // text is a state text with input value
checkBtn()
and after a click button in app i activate getUserName(text), this function pass the text in username state and here is a problem
when i try to see this text console.log(username) - it's give me null
but it if i try to see value console.log(value) - i see my input text
i don't understand how to fix that
react setState is async, which means those state variables are updated in the NEXT RENDER CYCLE(think of it as a thread or buffer).
try running this code if you want to understand what is happening BEHIND THE SCENES.
let renderCount = 0;
function TestApp() {
renderCount++;
const [state, setState] = useState(0);
const someRef = useRef(0);
someRef.current = state;
const someCallback = () => {
const someValue = new Date().getTime();
setState(someValue);
console.log(someRef.current, renderCount);
setTimeout(() => {
console.log(someRef.current, renderCount);
},100)
}
return <button onClick={someCallback}>clickme<button>;
}

persist state after page refresh in React using local storage

What I would like to happen is when displayBtn() is clicked for the items in localStorage to display.
In useEffect() there is localStorage.setItem("localValue", JSON.stringify(myLeads)) MyLeads is an array which holds leads const const [myLeads, setMyLeads] = useState([]); myLeads state is changed when the saveBtn() is clicked setMyLeads((prev) => [...prev, leadValue.inputVal]);
In DevTools > Applications, localStorage is being updated but when the page is refreshed localStorage is empty []. How do you make localStorage persist state after refresh? I came across this article and have applied the logic but it hasn't solved the issue. I know it is something I have done incorrectly.
import List from './components/List'
import { SaveBtn } from './components/Buttons';
function App() {
const [myLeads, setMyLeads] = useState([]);
const [leadValue, setLeadValue] = useState({
inputVal: "",
});
const [display, setDisplay] = useState(false);
const handleChange = (event) => {
const { name, value } = event.target;
setLeadValue((prev) => {
return {
...prev,
[name]: value,
};
});
};
const localStoredValue = JSON.parse(localStorage.getItem("localValue")) ;
const [localItems] = useState(localStoredValue || []);
useEffect(() => {
localStorage.setItem("localValue", JSON.stringify(myLeads));
}, [myLeads]);
const saveBtn = () => {
setMyLeads((prev) => [...prev, leadValue.inputVal]);
// setLocalItems((prevItems) => [...prevItems, leadValue.inputVal]);
setDisplay(false);
};
const displayBtn = () => {
setDisplay(true);
};
const displayLocalItems = localItems.map((item) => {
return <List key={item} val={item} />;
});
return (
<main>
<input
name="inputVal"
value={leadValue.inputVal}
type="text"
onChange={handleChange}
required
/>
<SaveBtn saveBtn={saveBtn} />
<button onClick={displayBtn}>Display Leads</button>
{display && <ul>{displayLocalItems}</ul>}
</main>
);
}
export default App;```
You've fallen into a classic React Hooks trap - because using useState() is so easy, you're actually overusing it.
If localStorage is your storage mechanism, then you don't need useState() for that AT ALL. You'll end up having a fight at some point between your two sources about what is "the right state".
All you need for your use-case is something to hold the text that feeds your controlled input component (I've called it leadText), and something to hold your display boolean:
const [leadText, setLeadText] = useState('')
const [display, setDisplay] = useState(false)
const localStoredValues = JSON.parse(window.localStorage.getItem('localValue') || '[]')
const handleChange = (event) => {
const { name, value } = event.target
setLeadText(value)
}
const saveBtn = () => {
const updatedArray = [...localStoredValues, leadText]
localStorage.setItem('localValue', JSON.stringify(updatedArray))
setDisplay(false)
}
const displayBtn = () => {
setDisplay(true)
}
const displayLocalItems = localStoredValues.map((item) => {
return <li key={item}>{item}</li>
})
return (
<main>
<input name="inputVal" value={leadText} type="text" onChange={handleChange} required />
<button onClick={saveBtn}> Save </button>
<button onClick={displayBtn}>Display Leads</button>
{display && <ul>{displayLocalItems}</ul>}
</main>
)

How to disable button based on state of other button

I'm trying to create 2 buttons in react app using material ui buttons with both buttons are enabled at the start of page load. When onClick on one of the button, the other button should be disabled vice versa.
Initial state
When onClick
const [btn1, setBtn1] = useState(false);
const [btn2, setBtn2] = useState(false);
const onBtn1 = () => {
setBtn1(!btn1);
};
const onBtn2 = () => {
setBtn2(!btn2);
};
}
How do i go about doing it? is there anyway to just use a single useState hook to handle 2 state buttons?
You can achieve this with only one state variable and one function
Code
const [disabledButton, setDisabledButton] = useState('');
const onButtonClick = (param) => {
setDisabledButton(param);
}
<Button onClick={() => onButtonClick('btn2')} disabled={disabledButton === 'btn1'}>
Button 1
</Button>
<Button onClick={() => onButtonClick('btn1')} disabled={disabledButton === 'btn2'}>
Button 2
</Button>
You can just enable the other button when a button is clicked:
const onBtn1 = () => {
setBtn1(prevState => !prevState);
setBtn2(false);
};
const onBtn2 = () => {
setBtn2(prevState => !prevState);
setBtn1(false);
};
in the JSX:
<button onClick={onBtn1} disabled={btn1}>btn1</button>
<button onClick={onBtn2} disabled={btn2}>btn2</button>
Change the state of other button on press.
const [btn1, setBtn1] = useState(true); //Initially both buttons are enabled
const [btn2, setBtn2] = useState(true);
const onBtn1 = () => {
setBtn1(!btn1);
setBtn2(false);
};
const onBtn2 = () => {
setBtn2(!btn2);
setBtn1(false);
};
}
you can use a single state, please refer the suggestion below:
state and event handlers-
const [activeButton, setActiveButton] = useState("None");
const onBtn1 = () => {
setActiveButton("Button1");
};
const onBtn2 = () => {
setActiveButton("Button2");
};
HTML Element part -
<Button disabled={!['None', 'Button1'].includes(activeButton)}>Button1</Button>
<Button disabled={!['None', 'Button2'].includes(activeButton)}>Button2</Button>

How can I change 2 different variables independently with a single function in React?

I'm new to React and I have a short and stupid question, but my poor phrasing makes it so that I haven't been able to find the answer by searching for it.
Basically, I have 2 password fields. I want to show and hide each one independently, but I would like a more elegant way than having 2 different functions with their own variables like this:
const [showPassword1, setShowPassword1] = useState(false);
const [showPassword2, setShowPassword2] = useState(false);
const togglePasswordVisiblity1 = () => {
setShowPassword1(showPassword1 ? false : true);
};
const togglePasswordVisiblity2 = () => {
setShowPassword2(showPassword2 ? false : true);
};
With the respective buttons below:
<span onClick={togglePasswordVisiblity1}>Show/Hide</span>
<span onClick={togglePasswordVisiblity2}>Show/Hide</span>
I'm sure there's a way to regroup these into a single function that changes the right variable based on which span is clicked, but I haven't had any luck finding the syntax. Sorry again for this question, hopefully it can be answered quickly!
Thanks in advance for your help.
You can try to use an array for the state. Check this sandbox demo:
import React, { useState } from "react";
export default function App() {
const [showPassword, setShowPassword] = useState([false, false]);
return (
<div className="App">
<button
onClick={() => setShowPassword([!showPassword[0], showPassword[1]])}
>
{JSON.stringify(showPassword[0])}
</button>
<button
onClick={() => setShowPassword([showPassword[0], !showPassword[1]])}
>
{JSON.stringify(showPassword[1])}
</button>
</div>
);
}
Refactored version by extracting state update into a function:
import React, { useState } from "react";
export default function App() {
const [showPassword, setShowPassword] = useState([false, false]);
const togglePassword = (idx) => {
const newShowPassword = [...showPassword];
newShowPassword[idx] = !newShowPassword[idx]; // toggle
setShowPassword(newShowPassword); // update the state
};
return (
<div className="App">
<button onClick={() => togglePassword(0)}>
{JSON.stringify(showPassword[0])}
</button>
<button onClick={() => togglePassword(1)}>
{JSON.stringify(showPassword[1])}
</button>
</div>
);
}
const [state , setState] = useState({
showPassword1:false,
showPassword2: false
})
const togglePasswordVisiblity1= e => {
const {name , value} = e.target
setState( prevState => ({
...prevState,
[name]: prevState[name] ? false : true
}))
}
//
<span name='showPassword1' onClick={togglePasswordVisiblity1}>Show/Hide</span>
<span name='showPassword2' onClick={togglePasswordVisiblity1}>Show/Hide</span>

Reactjs- How to fix after posting a new note via axios to the server, the render displays nothing?

I am learning to alter data in the backend, json server. The data gets posted if I refresh the server but the render of the new list disappears from the screen after the submit button. Why did the displaying of the notes stop after I submitted a new note ?
const Note = ({note, toggleImportance}) => {
const label = note.important ? 'make not important' : 'make important'
return(
<li >
{note.content}
<button onClick = {toggleImportance}>{label}</button>
</li>
)
}
export default Note
const App = () =>{
const [notes,setNotes] = useState([])
const [newNote, setNewNote] = useState('')
const [showAll, setShowAll] = useState(true)
useEffect(() => {
console.log('effect')
axios
.get('http://localhost:3002/notes')
.then(response => {
console.log('promise fulfilled')
setNotes(response.data)
})
},[])
console.log('render', notes.length, 'notes')
const toggleImportanceOf = id => {
const url = `http://localhost:3002/notes/${id}`
const note = notes.find(n => n.id === id)
const changedNote = {...note, important : !note.important}
axios
.put(url,changedNote)
.then(response => {
setNotes(notes.map(note => note.id !== id ? note : response.data))
})
}
const addNote =(event) =>{
event.preventDefault()
const newObject = {
content: newNote,
date: new Date().toISOString(),
important: Math.random() > 0.5
}
axios
.post('http://localhost:3002/notes', newObject)
.then(response => {
setNotes(notes.concat(response.data))
setNewNote('')
})
}
const handleNoteChange = (event) => {
setNewNote(event.target.value)
}
const notesToShow = showAll ? notes : notes.filter(note => note.important)
const rows = () => {
notesToShow.map(note =>
<Note
key={note.id}
note={note}
toggleImportance = {() => toggleImportanceOf(note.id)}
/>)
}
return(
<div>
<h1>Notes</h1>
<div>
<button onClick = {() => setShowAll(!showAll)}>
show{showAll ? ' important' : ' all'}
</button>
</div>
<ul>
{rows()}
</ul>
<form onSubmit={addNote}>
<input value={newNote} onChange={handleNoteChange}/>
<button type="submit">save</button>
</form>
</div>
)
}
export default App;
How do I fix this ? I want the new notes list to appeaar on the screen but it doesn't.
I forgot to enclose <Note /> in the rows() function with return()

Resources