onClick not changing useState in REACTJS - reactjs

The method closeBet does not seem to trigger anything when I click it. Could anyone explaining me why? I think I implemented everything properly just does not understand why is not working properly. Also even the other useState isActive is not doing its job. So what Should I do in order to change useState properly onClick.Thanks in advance
import React, { useState } from 'react';
import Button from '#material-ui/core/Button';
import FilterMenu from "./selectButton";
import FetchRandomBet from "./fetchRandomBets";
function Betslip() {
const data = [
{
value: 0,
label: "No Filter"
},
{
value: 1,
label: "Less than two"
},
{
value: 2,
label: "More than two"
},
]
const [selectedValue, setSelectedValue] = useState(0);
const [allStakes, setAllStakes] = useState(null);
const [isActive, setActive] = useState("false");
const handleChange = obj => {
setSelectedValue(obj.value);
}
const betNow = () => {
if (!allStakes) {
const stakes = localStorage.getItem("stakes");
const jsnStake = JSON.parse(stakes) || [];
setAllStakes([jsnStake]);
setActive(isActive);
console.log('yes')
} else if (allStakes) {
localStorage.setItem("stakes", null);
setAllStakes([])
console.log('no')
}
}
const closeBet = () => {
setActive("false");
}
console.log(allStakes)
return (
<div className="betslip">
<div className="betslip-top">
<h1 className="text">BETSLIP</h1>
<p className="text-two">BET WITH US!</p>
<div>
<FilterMenu
optionsProp={data}
valueProp={selectedValue}
onChangeProp={handleChange}
/>
</div>
</div>
<div>
<FetchRandomBet
valueProp={selectedValue}
/>
</div>
<Button
onClick={betNow}
className="betnow"
variant="contained"
>
Bet Now!
</Button>
<div className={isActive ? "bet-show" : "bet-noshow"}>
<button
onClick={closeBet}>
x
</button>
<h1>
{allStakes}
</h1>
</div>
</div >
);
}
export default Betslip;

You are using string instead of boolean value for your isActive state. It should be like this:
const [isActive, setActive] = useState(false);
and when updating the state:
const closeBet = () => {
setActive(false);
}
Remove quotes around false value and it should work as expected.

You probably want to set the value to false, so you should not use quotes as it becomes a string then.
setActive(false);

Related

Why the React List component gets rendered twice?

I have a React code as below:
import React, { useState, useCallback } from "react";
const initialUsers = [
{
id: "1",
name: "foo",
},
{
id: "2",
name: "bar",
},
];
const List = React.memo(({ users, onRemove }) => {
console.log("rendering list");
return (
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name} <span onClick={() => onRemove(user.id)}>X</span>
</li>
))}
</ul>
);
});
const App = () => {
const [users, setUsers] = useState(initialUsers);
const [text, setText] = useState("");
const handleRemove = useCallback(
(userId) => {
console.log("handleRemove", userId);
const filteredUsers = users.filter((user) => user.id !== userId);
setUsers(filteredUsers);
},
[users]
);
const handleText = (event) => {
setText(event.target.value);
};
return (
<div>
<input type="text" value={text} onChange={handleText} />
<List users={users} onRemove={handleRemove} />
</div>
);
};
export default App;
When I first load the page, I am seeing the rendering list got dumped twice in the browser console, see https://codesandbox.io/s/elegant-bash-ic9uqv?file=/src/App.js
Why is that?
That's because you have used StrictMode in your index.js
When the StrictMode is used, the lifecycle methods are called twice.
It happens for detecting unexpected side effects in your lifecycle methods.
Reference: https://reactjs.org/docs/strict-mode.html

localStorage is saving my data but after refresh is reseting and empty it

I have a problem and I need you to help me understand it. I am using ReactJS and I am building a simple CRUD Todo App. I Want to store my todos in local storage.
The data is saved there and I can see it but after the refresh it is emptying my local storage.
What am I doing wrong?
Something that I notice is that from the first time when I open the app (first rendering), local storage is creating the storage space without adding a todo.
Could I have missed something in my code that makes it reset it or empty it when the page is rendered?
import React, { useState, useEffect } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {
faCheck,
faPen,
faPlus,
faTrashCan,
} from "#fortawesome/free-solid-svg-icons";
import "./App.css";
import { faCircleCheck } from "#fortawesome/free-regular-svg-icons";
function App() {
const [todos, setTodos] = useState([]);
const [todo, setTodo] = useState("");
const [todoEditing, setTodoEditing] = useState(null);
const [editingText, setEditingText] = useState("");
useEffect(() => {
const json = window.localStorage.getItem("todos");
const loadedTodos = JSON.parse(json);
if (loadedTodos) {
setTodos(loadedTodos);
}
}, []);
useEffect(() => {
const json = JSON.stringify(todos);
window.localStorage.setItem("todos", json);
}, [todos]);
function handleSubmit(e) {
e.preventDefault();
const newTodo = {
id: new Date().getTime(),
text: todo,
completed: false,
};
setTodos([...todos].concat(newTodo));
setTodo("");
}
function deleteTodo(id) {
const updatedTodos = [...todos].filter((todo) => todo.id !== id);
setTodos(updatedTodos);
}
function toggleComplete(id) {
let updatedTodos = [...todos].map((todo) => {
if (todo.id === id) {
todo.completed = !todo.completed;
}
return todo;
});
setTodos(updatedTodos);
}
function submitEdits(id) {
const updatedTodos = [...todos].map((todo) => {
if (todo.id === id) {
todo.text = editingText;
}
return todo;
});
setTodos(updatedTodos);
setTodoEditing(null);
}
return (
<div className="App">
<div className="app-container">
<div className="todo-header">
<form onSubmit={handleSubmit}>
<input
type="text"
name="todo-input-text"
placeholder="write a todo..."
onChange={(e) => {
setTodo(e.target.value);
}}
value={todo}
/>
<button>
<FontAwesomeIcon icon={faPlus} />
</button>
</form>
</div>
<div className="todo-body">
{todos.map((todo) => {
return (
<div className="todo-wrapper" key={todo.id}>
{todo.id === todoEditing ? (
<input
className="edited-todo"
type="text"
onChange={(e) => setEditingText(e.target.value)}
/>
) : (
<p className={todo.completed ? "completed" : "uncompleted"}>
{todo.text}
</p>
)}
<div className="todo-buttons-wrapper">
<button onClick={() => toggleComplete(todo.id)}>
<FontAwesomeIcon icon={faCircleCheck} />
</button>
{todo.id === todoEditing ? (
<button onClick={() => submitEdits(todo.id)}>
<FontAwesomeIcon icon={faCheck} />
</button>
) : (
<button onClick={() => setTodoEditing(todo.id)}>
<FontAwesomeIcon icon={faPen} />
</button>
)}
<button
onClick={() => {
deleteTodo(todo.id);
}}
>
<FontAwesomeIcon icon={faTrashCan} />
</button>
</div>
</div>
);
})}
</div>
</div>
</div>
);
}
export default App;
You should be loading todos from localStorage on the Component mount if they are available in localStorage like this,
const loadedTodos = localStorage.getItem("todos")
? JSON.parse(localStorage.getItem("todos"))
: []; // new
const [todos, setTodos] = useState(loadedTodos); // updated
And then you don't have to mutate the state using setTodos(loadedTodos) in the useEffect.
Just remove this useEffect , from the code:
// that useEffect should be removed
useEffect(() => {
const json = window.localStorage.getItem("todos");
const loadedTodos = JSON.parse(json);
if (loadedTodos) {
setTodos(loadedTodos);
}
}, []);
You can check this in the working CodeSandbox as well.
I think your second useEffect is causing it to reset.
Move that the useEffect logic to a separate function.
And instead of calling setTodos, call that function, update the storage, and then call setTodos from that function.
If you call the setTodos function with a callback function and spread operator like this it should work:
useEffect(() => {
const json = window.localStorage.getItem("todos");
const loadedTodos = JSON.parse(json);
if (loadedTodos) {
// set local storage like this
setTodos( prevTodos => [...prevTodos, ...loadedTodos] );
}}, []);

How to add multiple events in one tag?

I'm making a counting timer which is described below with this react functional component
import {useEffect, useState, useRef} from 'react'
function Content() {
const [countdown, setCountdown] = useState(10)
const [show, setShow] = useState(true)
const ref = useRef()
function handleStart() {
ref.current = setInterval(() => {
setCountdown(prev => prev - 1)
}, 1000)
}
function handleStop() {
clearInterval(ref.current)
}
return (
<div>
<h2 style={{padding: 20}}>Time remaining: {countdown}</h2>
<button onClick={handleStart}>Start</button>
<button onClick={handleStop}>Stop</button>
</div>
)
}
export default Content;
How do I hide these two buttons after clicking one of the two.
Assuming show is the variable to control whether the buttons are visible or not.
<div>
<h2 style={{padding: 20}}>Time remaining: {countdown}</h2>
{show && <>
<button onClick={() => {
setShow(false)
handleStart()
}}>Start</button>
<button onClick={() => {
setShow(false)
handleStop()
}}>Stop</button>
</>}
</div>
React children need to return one element, so you can either wrap it in a div, or an empty element, <> </>, so you can return multiple nodes without adding a div, span, etc.
show && <></> means if show is true, the right-hand side will render, otherwise, it won't be rendered.
First, you have to introduce new state variable, you need one ror the start btn and another for the stop btn.
You have to setShow to false on either click and render the buttons conditionally depending on show variable:
const [countdown, setCountdown] = useState(10)
const [showStart, setShowStart] = useState(true)
const [showStop, setShowStop] = useState(true);
const ref = useRef()
function handleStart() {
setShowStart(false);
ref.current = setInterval(() => {
setCountdown(prev => prev - 1)
}, 1000)
}
function handleStop() {
setShowStop(false);
clearInterval(ref.current)
}
return (
<div>
<h2 style={{padding: 20}}>Time remaining: {countdown}</h2>
{showStart && <button onClick={handleStart}>Start</button>}
{showStop && <button onClick={handleStop}>Stop</button>}
</div>
)
Hope the Below Code Solver Your Problem
import React, { useEffect, useState, useRef } from 'react';
function Example() {
const [countdown, setCountdown] = useState(10);
const [show, setShow] = useState(true);
const ref = useRef();
function handleStart() {
setShow(!show);
ref.current = setInterval(() => {
setCountdown((prev) => prev - 1);
}, 1000);
}
function handleStop() {
setShow(!show);
clearInterval(ref.current);
}
return (
<div>
<h2 style={{ padding: 20 }}>Time remaining: {countdown}</h2>
{show && (
<div>
<button onClick={handleStart}>Start</button>
<button onClick={handleStop}>Stop</button>
</div>
)}
</div>
);
}
export default Example;

A method triggers a console.log from another component

When I click on bet now the function triggers a console.log from another component. betNow should group all the inputs from stake in one common array but when I click on it it renders the console log from stake and includes all the values that I typed into one array. Everything works but not as I wish. The parent component should display the common array with all the values. I do not understand why it is happening.Could anyone explain me why is reacting like that? Thanks in advance
Parent Component
import React, { useState } from 'react';
import Button from '#material-ui/core/Button';
import FilterMenu from "./selectButton";
import FetchRandomBet from "./fetchRandomBets";
function Betslip() {
const data = [
{
value: 0,
label: "No Filter"
},
{
value: 1,
label: "Less than two"
},
{
value: 2,
label: "More than two"
},
]
const [selectedValue, setSelectedValue] = useState(0);
const [allStakes, setAllStakes] = useState([]);
const handleChange = obj => {
setSelectedValue(obj.value);
}
const betNow = () => {
const stakes = localStorage.getItem("stakes");
const jsnStake = JSON.parse(stakes) || [];
setAllStakes([...allStakes, jsnStake]);
}
return (
<div className="betslip">
<div className="betslip-top">
<h1 className="text">BETSLIP</h1>
<p className="text-two">BET WITH US!</p>
<div>
<FilterMenu
optionsProp={data}
valueProp={selectedValue}
onChangeProp={handleChange}
/>
</div>
</div>
<div>
<FetchRandomBet
valueProp={selectedValue}
/>
</div>
<Button
onClick={betNow}
className="betnow"
variant="contained"
>
Bet Now!
</Button>
</div>
);
}
export default Betslip;
Child Component
import React, { useState, useEffect } from 'react';
function Stake() {
const [stakes, setStakes] = useState([]);
const addStake = (e) => {
e.preventDefault();
const newStake = e.target.stake.value;
setStakes([newStake]);
};
useEffect(() => {
const json = JSON.stringify(stakes);
localStorage.setItem("stakes", json);
}, [stakes]);
console.log(stakes)
return (
<div>
<form onSubmit={addStake}>
<input
style={{
marginLeft: "40px",
width: "50px"
}}
type="text"
name="stake"
required
/>
</form>
</div>
);
}
export default Stake;
You have this console.log in you function that will run every time the component is rendered, since it´s outside of any function:

Dynamically add component in react with hooks

i have 3 components: Form (parent), Picklist and ParagraphBox (children); based on the select of the picklist, i render ParagraphBox and also a "+" button. What i would like to achieve is on the click of the plus button, render another ParagraphBox, just under the first. I would also like the remove functionality.
My ParagraphBox component has a title and a content, and i want to give the adding a progressive number:
e.g Paragraph 1
Content: ....
Paragraph 2
Content: ....
And so on
Here's my ParagraphBox component:
import React, { useState, useEffect } from 'react';
export default function ParagraphBox(props) {
const [paragrafo, setParagrafo] = useState({})
useEffect(() => {
console.log('paragrafo ', paragrafo)
props.onChange(paragrafo)
}, [paragrafo])
const onChange = (e) => {
const titolo = e.target.name
const contenuto = e.target.value
setParagrafo({
...paragrafo,
[titolo]: contenuto
})
}
return (
<div className = "paragraph-box">
<label>
{props.labelInputBox}
<div>
<input type="text" name="titolo" value={paragrafo.titolo || ''} onChange={onChange}/>
</div>
{props.labelTextArea}
<div>
<textarea id="subject" name="contenuto" placeholder="Inserisci contenuto.." style={{height: "45x", width: "400px"}} value={paragrafo.contenuto || ''} onChange={onChange} />
</div>
</label>
</div>
)
}
Here is my Form component:
import React, { useState, useEffect, useRef } from 'react';
import './Form.css'
import createDocument from '../pdfTool';
import finalita from '../icons/finalita.PNG';
import Picklist from './Picklist.js';
import ParagraphBox from './ParagraphBox';
export default function Form() {
const [flagImg, setFlagImg] = useState(false)
const [flagPar, setFlagPar] = useState(false)
const [paragArray, setParagArray] = useState([
{titolo: '', contenuto: ''}
])
const handleChange = (e) => {
console.log('e ', e)
console.log('e.titolo PARENT ', e.titolo)
console.log('e.contenuto PARENT ', e.contenuto)
setParagArray({
...paragArray,
[e.titolo]: e.contenuto
})
}
useEffect(() => {
console.log('rendering useEffect')
console.log('flagPar: ', flagPar)
console.log('flagImg: ', flagImg)
console.log('paragArray ', paragArray)
}, [flagPar, flagImg, paragArray])
const handleSubmit = (evt) => {
evt.preventDefault(); //usato per evitrare il refresh del browser
}
const addParag = (parag) => {
console.log('paragArray PARENT ', paragArray)
}
const onSelect = (selectedValue) => {
console.log('valore selezionato nella picklist: ' + selectedValue)
if(selectedValue === 'Layout 2') {
setFlagImg(true)
setFlagPar(true)
}
}
return(
<div>
<Picklist onSelect={onSelect} label="Seleziona un layout di contratto: " pickVals={["Seleziona...", "Layout 1", "Layout 2", "Layout 3"]}/>
{flagImg ? (
<form onSubmit={handleSubmit}>
<Picklist onSelect={onSelect} label="Seleziona Immagine: " pickVals={["Seleziona...", "Immagine 1", "Immagine 2", "Immagine 3"]} />
</form>
) : null}
{flagPar ? (
<div>
<ParagraphBox labelInputBox="Paragfrafo 1" labelTextArea="Contenuto Paragrafo" onChange={handleChange}/>
<div id = "add-paragraph">
<button type="button" onClick={addParag}>+</button>
<input type="submit" value="Submit" />
</div>
</div>
) : null}
</div>
)
Thanks in advance for your time
I know this is old...but I just faced the same issue, so here it goes: JSX is just syntactic sugar for regular JavaScript. Therefore you can just create the component manually and make it available as part of your hook, i.e.:
custom hook:
import React, { useState } from 'react';
import Advise from '../../components/Advise/Advise';
const useAdvise = () => {
const [ showAdvise, setShowAdvise ] = useState(false)
const [ adviseMsg, setAdviseMsg ] = useState('')
const [ loading, setLoading ] = useState(false)
const hideAdvise = () => {
setShowAdvise(false)
}
const adviseComponent = React.createElement(Advise, {show:showAdvise, showSpinner:loading, hideAdvise:hideAdvise, children:adviseMsg})
return {
adviseComponent,
setShowAdvise,
setAdviseMsg,
setLoading
}
};
export default useAdvise;
component where I want it:
import useAdvise from '../hooks/useAdvise/useAdvise'
const Page = () => {
const {adviseComponent, setShowAdvise, setAdviseMsg, setLoading} = useAdvise()
return(
<div>
{adviseComponent}
</div>
)
}
hope it helps (cheers from Br as well)

Resources