Hello Guys This is how my code looks like
export default function Billing() {
const classes = useStyles();
const [balance, setBalance] = useState('0.00');
const [upcoming, setUpcoming] = useState('0.00');
const [lastmonth, setLastmonth] = useState('0.00');
const [header, setHeader] = useState('Please Enter The Amount');
const [open, setOpen] = useState(false);
const [amountstate, setAmountstate] = useState(false);
const [amounthelper, setAmounthelper] = useState('');
const [sairam, setSairam] = useState(0);
const onchange = async (e) => {
console.log(sairam)
setSairam({amount: e.target.value})
}
const [modalchild, setModalchild] = useState(<>
<form noValidate autoComplete="off">
<TextField
error={amountstate}
type="number"
value={sairam}
onChange={onchange}
label='Please enter the amount'
helperText={amounthelper}
variant="outlined"
/>
<br />
<br />
<Button variant="contained" color="primary" onClick={() => addBalance()}>Add Balance</Button>
</form>
</>);
const [country, setCountry] = useState(false);
const [currency, setCurrency] = useState('');
const addBalance = () => {
console.log("Clicked :)")
console.log(sairam) // outputs the inital value not the changed value
});
return(<>
<div className="balance-container">
<div className="columns-top">
<div className="column-top">
<h1>${upcoming}</h1>
</div>
<div className="column-top">
<h1>${balance}</h1>
<Button variant="contained" color="primary" onClick={handleOpen}>Add Balance</Button>
</div>
<div className="column-top">
<h1>${lastmonth}</h1>
</div>
</div>
</div>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<div className={classes.paper}>
<h2 id="transition-modal-title">{header}</h2>
{modalchild}
</div>
</Fade>
</Modal>
</>
)
}
if I change the state sairam by using the function setSairam() the state is not getting updated and its only logging the initial state which is 0 i have even tried the loggint the event value of my input it works correct but the state alone is not changing please help me feel free to give me any other options
setSairam() is the asynchronous method so you can't get the updated value of sairam immediately after setSairam().
const onchange = async (e) => {
setSairam({amount: e.target.value})
console.log(sairam) // This will console old value of sairam
}
You should use useEffect with adding a sairam dependency to check the updated state value.
useEffect(() => {
console.log(sairam)
}, [sairam]);
IMPORTANT
sairam will be the value of the TextField, so you should update the sairam to the string value, not object.
setSairam(e.target.value)
You will not get the updated value in your onChange function because that function is not aware when state is changed.
Use useCallback to get the updated value of sairam. Pass sairam in the dependency array.
const onchange = useCallback(async (e) => {
console.log(sairam)
setSairam({amount: e.target.value})
},[sairam]);
or use prevState
setSairam(prevState => {
console.log(sairam)
return ({amount: e.target.value})
});
Another option is to pass sairam to onChange function. But I am not sure how it works since your markup is stored in state
onChange={(e)=>onchange(e, sairam)}
const onchange = async (e, sairam) => {
console.log(sairam)
setSairam({amount: e.target.value})
}
Related
Within a child component I have state to store input.
My goal is to pass the input state to the parent component.
//Child component
const [userInput, setUserInput] = useState("");
<TextField value={input} onInput={e => setInput(e.target.value)} />
I tried creating a function to be called on input within the parent function but I cannot seem to get the value "input" passed. What is the correct way to do this? Thanks!
you were approaching correctly. You can pass a function to get the data from the child to the parent. Check this example:
import { useState } from "react";
import "./styles.css";
export default function App() {
const [text, setText] = useState("");
const getInput = (t) => {
console.log(t);
setText(t);
};
return (
<div className="App">
<Component getInput={getInput} value={text} />
<p>{text}</p>
</div>
);
}
const Component = ({ getInput, value }) => {
return (
<>
<h1> Test </h1>
<input type="text" value={value} onChange={(e) => getInput(e.target.value)}></input>
</>
);
};
The parent App is retrieving the text from the child Component passing the function getInput. You only have to decide with event you want to detonate. Of course, this is with input not with TextField. I think that you are using TextField from React Material UI, if that's the case you may check the docs to know how to trigger an event.
You can move the state to the parent component. Then pass the props from the child to the parent.
function Child({
...rest
}) {
return <TextField {...rest} />
}
function Parent() {
const [input, setInput] = useState(''); // '' is the initial state value
return (
<Child value={value} onInput={e => setInput(e.target.value)} />
)
}
I finally figured this out.
// This is in the parent component
const [userInput, setUserInput] = useState("");
// Step 1: Create a function that handles setting the state
const inputHandler = (userInput) => {
setUserInput(userInput);
}
<PhaseTwoTemplate onInput={inputHandler} />
//Step 2: Add a function that calls the handler
...
// This is the child component PhaseTwoTemplete.js
const [input, setInput] = useState("");
// This second state holds the input data for use on child component
// It also is an easy way to hold the data for the parent function.
<TextField
onChange={e => setInput(e.target.value)}
value={input}
onInput={onInputHandler}
/>
// Step 3: Pass the user input to the parent
const onInputHandler = () => {
props.onInput(input);
}
I am trying to create a simple React form that, on button click, will display the entered input value in a controlled input element. Specifically, I do NOT want to have an identical solution to that in the React docs (constantly updating the displayed value on input change), rather I only want it to update the displayed paragraph text after the user has hit the submit button. I am able to do this with this current solution (conditionally rendering based on submitted state that is set in handler functions):
import { useState } from 'react';
export default function App() {
const [text, setText] = useState('');
const [submitted, setSubmitted] = useState(false);
const handleSubmit = e => {
e.preventDefault();
setSubmitted(true);
};
const handleChange = e => {
setSubmitted(false);
setText(e.target.value);
};
return (
<>
<form onSubmit={e => handleSubmit(e)}>
<label>Text: </label>
<input type="text" value={text} onChange={e => handleChange(e)} />
<button type="submit" onClick={handleSubmit}>
Show
</button>
{submitted && <p>{text}</p>}
</form>
</>
);
}
But I am guessing that there is a much better way to do this.
If you want to be able to submit multiple times and keep the last before submitting again, use this:
import { useState } from 'react';
export default function App() {
const [text, setText] = useState('');
const [displayText, setDisplayText] = useState('');
const handleSubmit = e => {
e.preventDefault();
setDisplayText(text);
};
const handleChange = e => {
setText(e.target.value);
};
return (
<>
<form onSubmit={e => handleSubmit(e)}>
<label>Text: </label>
<input type="text" value={text} onChange={e => handleChange(e)} />
<button type="submit" onClick={handleSubmit}>
Show
</button>
{displayText && <p>{displayText}</p>}
</form>
</>
);
}
displayText && ... is so that the paragraph tag doesn't exist until it has a value to display, but you can just replace that section with out that and it will work.
I have this Component:
export const DashboardPage = () => {
const [mounted, setMounted] = useState(false);
const [currentNumber, setCurrentNumber] = useState(null);
const [insertedNumber, setInsertedNumber] = useState(null);
const [result, setResult] = useState(null);
const [arrayNumbers, setArrayNumbers] = useState([]);
useEffect(() => {
const rnd = randomNumber();
if (!mounted) {
setCurrentNumber(rnd);
setMounted(true);
}
}, [mounted]);
const handleChange = (e) => {
setInsertedNumber(e.target.value);
}
const handleClick = (e) => {
e.preventDefault();
arrayNumbers.push(insertedNumber);
setArrayNumbers(arrayNumbers);
setResult("Oops, ritenta! era " + currentNumber)
if (parseInt(insertedNumber) === parseInt(currentNumber)) {
setResult("EVVIVA hai vinto!");
}
}
const handleOnSubmit = (e) => {
e.preventDefault();
}
return (
<>
<h2>Storico tentativi: {arrayNumbers.map(e => e)}</h2>
<h2>La soluzione è: {currentNumber}</h2>
<Form onSubmit={handleOnSubmit}>
<Form.Group controlId="formGTN">
<Form.Label>Guess the number:</Form.Label>
<Form.Control type="text" onChange={handleChange} />
</Form.Group>
<Button variant="primary" type="submit" onClick={handleClick} label="Is it?" />
</Form>
<h1>{result}</h1>
</>
)
}
export default DashboardPage;
It is a simple "Guess The Number" play.
I have the issue that:
On very first loading of page, I can get into arrayNumbers the only one number I inserted.
From second and other attempts, when I CHANGE the value inside the input, so when I delete previous number, I get state update and I have the previous (and on) numbers. Like if "state" is "back" of one try.
I can see this behavious also in Chrome's Redux plugin. State is update ONLY a step other.
I could move the push into array on onChange, but... If I insert a number of 2 digits I get 2 insert on array (44 will be "4" and "4").
THank you!
You should never mutate the state and/or update the state without setState. In JavaScript, the Array.push() method mutates the original array. So, in your example, you mutate arrayNumbers but you should add a new element to the array without updating the original one.
Also, listening to the onSubmit event is enough, you don't have to add the onClick event to your submit button. You can move everything from handleClick to handleOnSubmit.
export const DashboardPage = () => {
const [mounted, setMounted] = useState(false);
const [currentNumber, setCurrentNumber] = useState(null);
const [insertedNumber, setInsertedNumber] = useState(null);
const [result, setResult] = useState(null);
const [arrayNumbers, setArrayNumbers] = useState([]);
useEffect(() => {
const rnd = randomNumber();
if (!mounted) {
setCurrentNumber(rnd);
setMounted(true);
}
}, [mounted]);
const handleChange = (e) => {
setInsertedNumber(e.target.value);
};
const handleOnSubmit = (e) => {
e.preventDefault();
setArrayNumbers([...arrayNumbers, insertedNumber]);
if (parseInt(insertedNumber, 10) === parseInt(currentNumber, 10)) {
setResult('EVVIVA hai vinto!');
} else {
setResult('Oops, ritenta! era ' + currentNumber);
}
};
return (
<>
<h2>Storico tentativi: {arrayNumbers.map((e) => e)}</h2>
<h2>La soluzione è: {currentNumber}</h2>
<Form onSubmit={handleOnSubmit}>
<Form.Group controlId="formGTN">
<Form.Label>Guess the number:</Form.Label>
<Form.Control type="text" onChange={handleChange} />
</Form.Group>
<Button variant="primary" type="submit" label="Is it?" />
</Form>
<h1>{result}</h1>
</>
);
};
For the above component, you might try these changes in the handleClick function:
const handleClick = (e) => {
e.preventDefault();
let newArr = [...arrayNumbers];
newArr.push(insertedNumber);
setArrayNumbers(newArr);
setResult('Oops, ritenta! era ' + currentNumber);
if (parseInt(insertedNumber) === parseInt(currentNumber)) {
setResult('EVVIVA hai vinto!');
}
};
Let's say I have an Input component with onChange event handler in it, say count input characters(just to illustrate there's a build-in functionality in that component that I want to use no matter what) and I want to use this Input like across all of my other components:
const Input = (props) => {
const { name, value, countLimit, ...restProps } = props;
const [count, setCount] = useState(0);
const countCharacters = (e) => {
setCount(e.currentTarget.value.length);
};
return (
<div>
<input
name={name}
value={value}
onChange={countCharacters}
{...restProps}
/>
<br />
Count: {count} / {countLimit}
</div>
);
};
The problem though, is that if I want to pass an onChange event handler from outside this component, say to grab some input's value or pass some data from child to parent or something else, it will override this countCharacters one. What is the best way to deal with this kind of scenarios?
Invoke props.onChange (if it exists) from within countCharacters:
const Input = (props) => {
const { name, value, countLimit, onChange, ...restProps } = props;
const [count, setCount] = useState(0);
const countCharacters = (e) => {
setCount(e.currentTarget.value.length);
onChange?.(e);
};
return (
<div>
<input
name={name}
value={value}
onChange={countCharacters}
{...restProps}
/>
<br />
Count: {count} / {countLimit}
</div>
);
};
I am trying to use the state hook in my react app.
But setTodos below seems not updating the todos
link to my work: https://kutt.it/oE2jPJ
link to github: https://github.com/who-know-cg/Todo-react
import React, { useState } from "react";
import Main from "./component/Main";
const Application = () => {
const [todos, setTodos] = useState([]);
// add todo to state(todos)
const addTodos = message => {
const newTodos = todos.concat(message);
setTodos(newTodos);
};
return (
<>
<Main
addTodos={message => addTodos(message)}
/>
</>
);
};
export default Application;
And in my main.js
const Main = props => {
const input = createRef();
return (
<>
<input type="text" ref={input} />
<button
onClick={() => {
props.addTodo(input.current.value);
input.current.value = "";
}}
>
Add message to state
</button>
</>
);
};
I expect that every time I press the button, The setTodos() and getTodos() will be executed, and the message will be added to the todos array.
But it turns out the state is not changed. (still, stay in the default blank array)
If you want to update state of the parent component, you should pass down the function from the parent to child component.
Here is very simple example, how to update state with hook from child (Main) component.
With the help of a button from child component you update state of the parent (Application) component.
const Application = () => {
const [todos, setTodos] = useState([]);
const addTodo = message => {
let todosUpdated = [...todos, message];
setTodos(todosUpdated);
};
return (
<>
<Main addTodo={addTodo} />
<pre>{JSON.stringify(todos, null, 2)}</pre>
</>
);
};
const Main = props => {
const input = createRef();
return (
<>
<input type="text" ref={input} />
<button
onClick={() => {
props.addTodo(input.current.value);
input.current.value = "";
}}
>
Add message to state
</button>
</>
);
};
Demo is here: https://codesandbox.io/s/silent-cache-9y7dl
In Application.jsx :
You can pass just a reference to addTodos here. The name on the left can be whatever you want.
<Main addTodos={addTodos} />
In Main.jsx :
Since getTodo returns a Promise, whatever that promise resolves to will be your expected message.
You don't need to pass message as a parameter in Main, just the name of the function.
<Main addTodos={addTodos} />
You are passing addTodos as prop.
<Main
addTodos={message => addTodos(message)}
/>
However, in child component, you are accessing using
props.addTodo(input.current.value);
It should be addTodos.
props.addTodos(input.current.value);