I started to learn to react today, and I'm trying to build a super simple calculator, but I'm not familiar with the syntax and basics of reacting yet. No matter how much I'm looking and reading, my code is like this :
import './App.css';
const plusaction = (a, b) => {
alert(a+b);
}
function App() {
return (
<div className="App">
<input type="number" value={plusaction.a}></input>
<input type="number" value={plusaction.b}></input>
<button onClick={plusaction}>Result</button>
</div>
);
}
export default App;
As you can see, it was supposed to be a simple form plus action calculator, but the alert brought me "object undefined", Would you mind fixing my code and explaining what I did wrong? I appreciate any help you can provide.
First you must have a state to save data
after that, you must change your state with onChange function of input
after that, you must read your values from state
function App() {
const [state, setState] = useState({ a: 0, b: 0 });
const plusaction = () => {
alert(state.a + state.b);
};
return (
<div className="App">
<input type="number" value={state.a} onChange={(e) => setState({ ...state, a: e.target.value })} />
<input type="number" value={state.b} onChange={(e) => setState({ ...state, b: e.target.value })} />
<button onClick={plusaction}>Result</button>
</div>
);
}
export default App;
You should use useRef to do this. It will more efficient than useState which will re-render your app each time your number change.
import { useRef } from "react";
function App() {
const a = useRef(0);
const b = useRef(0);
const plusaction = () => {
console.log(a.current.value);
console.log(b.current.value);
alert(parseInt(a.current.value) + parseInt(b.current.value));
};
return (
<div className="App">
<input type="number" ref={a} />
<input type="number" ref={b} />
<button onClick={plusaction}>Result</button>
</div>
);
}
export default App;
you can simply use controlled Input like this :
import {useState} from "react";
function App() {
const [firstVal,setFirstVal] = useState('');
const [secondVal,setSecondVal] = useState('');
const handleShowResult = ()=>{
alert(parseInt(firstVal) + parseInt(secondVal))
}
return (
<div className="App">
<input type="number" value={firstVal} onChange={(e)=>setFirstVal(e.target.value)}></input>
<input type="number" value={secondVal} onChange={(e)=>setSecondVal(e.target.value)}></input>
<button onClick={handleShowResult}>Result</button>
</div>
);
}
export default App;
hope its helpful
You should use state to saving data.
import {useState} from "react";
function App() {
const [firstNumber, setFirstNumber] = useState(0);
const [secondNumber, setSecondNumber] = useState(0);
const plusaction = () => {
alert(firstNumber + secondNumber);
};
return (
<div className="App">
<input type="number" value={firstNumber} onChange={(e) => setFirstNumber(e.target.value)} />
<input type="number" value={secondNumber} onChange={(e) => setSecondNumber(e.target.value)} />
<button onClick={plusaction}>Result</button>
</div>
);
}
Ideally you want to want to store your input values in state. Here I've initialised the input state as an object which will later update with a and b properties containing the values of the inputs.
plusAction (or handleAdd as I've called it here) then just takes the a and b values from the input state and logs the sum to the console.
Give you input elements a name attribute so they can be identified easily.
const { useState } = React;
function Example() {
// Initialise state
const [ input, setInput ] = useState({});
// Destructure the a and b properties from
// the state and sum them
function handleAdd() {
const { a, b } = input;
console.log(a + b);
}
// The onChange listener is attached to the
// parent container so we need to check to see
// if the changed element is an input
function handleChange(e) {
if (e.target.matches('input')) {
// Destructure the name and value from the input
const { name, value } = e.target;
// Set the new input state by copying it, and
// updating either the a or b property we get from
// the name attribute, and then setting its value
// Note: the type of the value from an input will
// always be a string, so you need to coerce it to
// a number first
setInput({ ...input, [name]: Number(value) });
}
}
// The input elements store the value of the
// corresponding state property
return (
<div onChange={handleChange}>
<input name="a" type="number" value={input.a} />
<input name="b" type="number" value={input.b} />
<button onClick={handleAdd}>Result</button>
</div>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Additional documentation
Destructuring assignment
Spread syntax
matches
Related
I am working on a react POS app with Typescript. I want to calculate the change when the I enter a the amount of money received from a buyer. When I enter the value into the input element, the first value passed to the logic that calculates the change is an empty value then the other values are added. It causes an inaccurate calculation.
const CartBill: React.FC<{}> = () => {
const [change, setChange] = useState<number>(0)
//logic to calculate change
const calculateChange: changeCalculatorType = (amountReceived) => {
if (amountReceived <= +grandTotal.toFixed(2)) {
setChange(0);
} else {
const change = amountReceived - +grandTotal.toFixed(2);
setChange(+change.toFixed(2));
}
return change;
};
return(
<div> <Received onSetChange={calculateChange} /> </div>
<div>{change}</div>
)
}
this is the component that contains the input element. It lifts the amount state up to the CartBill component
import {useState} from 'react'
import { changeCalculatorType } from '../../types/ReceivedComponentTypes';
const Received: React.FC<{ onSetChange: changeCalculatorType }> = (props) => {
const [amount, setAmount] = useState(0);
const getInputHandler = (event: React.FormEvent<HTMLInputElement>) => {
const retrieved = +(event.target as HTMLInputElement).value.trim();
setAmount(retrieved);
props.onSetChange(amount);
};
return (
<>
<input type="number" name="" id="" onChange={getInputHandler} />
</>
);
};
export default Received
I tried trim()ing the value but that doesn't seem to work. Any help is hugely appreciated
You need to set the value attribute on your input.
return (
<input type="number" name="" id="" value={amount} onChange={getInputHandler} />
);
I am trying to write a simple program that allows you to enter your first and last name in input fields so you get a greeting based on your name. But I cannot get it to work.
import "./styles.css";
import React, { useState } from "react";
export default function App() {
const [Savedinput, setSavedinput] = useState({Fname:' ' , Lastname:''} );
const ChangeFirst = (e) => {
setSavedinput Savedinput.Fname(e.target.value);
};
const ChangeLast = (e) => {
setSavedinput Savedinput.Lastname(e.target.value);
console.log(e.target.value);
};
return (
<div className="App">
<input type="Text" onChange={ChangeFirst}></input>
<input type="Text" onChange={ChangeLast}></input>
<h1> hello {Fname} {Lastname} </h1>
</div>
);
}
You can use destructure method.
import "./styles.css";
import React, { useState } from "react";
export default function App() {
const [Savedinput, setSavedinput] = useState({Fname:' ' , Lastname:''} );
const onInputChange = (e, attr) =>{
setSavedinput({...Savedinput, [attr]: e.target.value});
}
return (
<div className="App">
<input type="Text" onChange={(e)=>{onInputChange(e, 'Fname)}} value={Savedinput.Fname}></input>
<input type="Text" onChange={(e)=>{onInputChange(e, 'Lastname)}} value={Savedinput.Lastname}></input>
<h1> hello {Savedinput.Fname} {Savedinput.Lastname} </h1>
</div>
);
}
You shouldn't mutate state directly
To change the a state object you can use spread operator. So your code would be something like:
const ChangeFirst = (e) => {
setSavedinput({...SavedInput, Fname: e.target.value})
};
const ChangeLast = (e) => {
setSavedinput({...SavedInput, Lastname: e.target.value})
};
The {...SavedInput} will expand the whole object to the argument and then adding the Fname: ... will overwrite that so the new value will be passed instead.
For more advance usage of form I recommend you to use react-hook-form
You can have 2 states for firstName and lastName
export default function App() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
return (
<div className="App">
<input type="Text" value={firstName} onChange={() => setFirstName(e.target.value)}></input>
<input type="Text" value={lastName} onChange={() => setLastName(e.target.value)}></input>
<h1> hello {firstName} {lastName} </h1>
</div>
);
}
<input type="Text" onChange={ChangeFirst}></input>
<input type="Text" onChange={ChangeLast}></input>
use two states
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
then respectively on your method use the setState for each of them separately
Firstly the setSavedinput is a function and not a variable. It's a function to set the value of the variable used with it. Since you are taking the value from input field the following method might work out for you.
import React, { useState } from "react";
export default function App() {
const [Savedinput, setSavedinput] = useState({Firstname:'' , Lastname:''} );
return (
<div className="App">
<input type="text" value={Savedinput.Firstname} onChange={(e)=>setSavedinput({... Savedinput , Firstname:e.target.value })} />
<input type="text" value={Savedinput.Lastname} onChange={(e)=>setSavedinput({... Savedinput , Lastname:e.target.value })} />
<h1> hello {Firstname} {Lastname} </h1>
</div>
Instead of declaring a function , you can do it inline for this purpose .
Also I would suggest you to keep the variable names in an order. This is gonna you a lot.
You can use this for the object and ...savedinput to maintain the existing object with updating only changed value
export default function App() {
const [savedinput, setSavedinput] = useState({firstname:"",
lastname:""});
function ChangeFirst (e) {
savedinput.firstname = e
setSavedinput({...savedinput})
};
function ChangeLast(e) {
savedinput.lastname = e
setSavedinput({...savedinput})
};
return (
<div className="App">
<input type="Text" onChange={event =>
ChangeFirst(event.target.value)}></input>
<input type="Text" onChange={event =>
ChangeLast(event.target.value)}>.
</input>
<h1> hello {savedinput.firstname }{savedinput.lastname} </h1>
</div>
);
}
You can find this working in this link
I'm having a problem on inputs than want to change in array, there is a lot of lag/delay when typing in inputs when this array have more than 8 arraylists, i make this code below to simple reproduce what's happen with my code.
App.js
import './App.css';
import React,{useState} from 'react';
import Inputs from './Inputs'
function App() {
const [ array, setArray ] = useState([
["John",'30','ttt'],
["Smith",'20','ttt'],
["Thiago",'40','ttt'],
["Jake",'30','ttt'],
["Fer",'41','ttt'],
["Fred",'23','ttt'],
["Otto",'55','ttt'],
["Gago",'21','ttt'],
["Higor",'15','ttt'],
]);
return (
<div className="App">
<header className="App-header">
<Inputs array={array} setArray={setArray} />
<br />
<button onClick={() => {
/** add More arrays */
setArray(array=> [...array, ['','','']])
}}>add more</button>
<br />
<button onClick={() => console.log("Send array to DB")}>Send array to DB</button>
</header>
</div>
);
}
export default App;
Inputs.js
import './App.css';
import React,{ useEffect } from 'react';
function Inputs(props) {
const { array, setArray } = props
const handleInputChange = (e, index, position2) => {
const {value} = e.target;
const list = [...array];
list[index][position2] = value;
setArray(list);
};
useEffect(() => {
console.log(array)
},[array])
return (
array.map((item, index) => {
return (<div key={index}>
<input
value={item[0]}
onChange={(e) => {
handleInputChange(e,index,0)
}}
/>
<input
value={item[1]}
onChange={(e) => {
handleInputChange(e,index,1)
}}
/>
<input
value={item[2]}
onChange={(e) => {
handleInputChange(e,index,2)
}}
/>
</div>)
})
);
}
export default Inputs;
I don't find another option to change value in this array, and each type in inputs are creating every arrays again.
Thanks
You can consider maintaining separate states for all the inputs instead of creating a common state.
A better alternative to this will be to create a pure component that can be used for rendering all the inputs - Then you will just have to pass the value and onChange callback.
I have a higher order component like this.
import React from 'react';
const NewComponent = ( WrappedComponent ) => {
class UpdatedComponent extends React.Component {
render() {
// Custom Hook
// const values = useCustomHook(InitialState);
return(
<WrappedComponent />
)
}
}
return UpdatedComponent;
};
export { NewComponent };
And the wrapped component like these.
const App = () => {
return(
<Form>
<input
type = 'text'
placeholder = 'Enter your name' />
<input
type = 'email'
placeholder = 'Enter your email' />
<button
type = 'submit'>
Submit
</button>
</Form>
)
}
The thing is i want to iterate through input elements in the wrapped components and construct a compound state, which i will pass a an Argument to the custom hook in the hoc? Is there a way to achieve this functionality?
I think you should not parse jsx and create data from that but you have not really demonstrated why you need to do this and maybe have a valid use case for it.
Here is how you could parse jsx (the html created from it):
const useCustomHook = (state) => {
if (state !== null) {
console.log('custom hook, state is:', state);
}
};
const Form = ({ children }) => {
const [state, setState] = React.useState(null);
const ref = React.useRef();
useCustomHook(state);
React.useEffect(() => {
debugger;
setState(
Array.from(ref.current.querySelectorAll('input')).map(
({ type, placeholder }) => ({
type,
placeholder,
})
)
);
}, []);
return <div ref={ref}>{children}</div>;
};
const App = () => {
return (
<Form>
<input type="text" placeholder="Enter your name" />
<input type="email" placeholder="Enter your email" />
<button type="submit">Submit</button>
</Form>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
This is a pretty common pattern in React components:
handleTextFieldChange(event)
{
const name = event.currentTarget.name;
this.setState({[name]: event.currentTarget.value})
}
What Javascript syntax could be used to do the same with React hooks?
i.e. something possibly along the lines of:
handleTextFieldChange(event)
{
const name = event.currentTarget.name;
this.set[name](event.currentTarget.value);
}
You could use a single useState with a default value of an object that contains all your input values and update that like you are used to with class components.
Example
const { useState } = React;
function App() {
const [state, setState] = useState({ email: "", password: "" });
function onChange(event) {
const { name, value } = event.target;
setState(prevState => ({ ...prevState, [name]: value }));
}
return (
<div>
<input value={state.email} name="email" onChange={onChange} />
<input value={state.password} name="password" onChange={onChange} />
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
How about something like this?
function handleTextFieldChange(mySetFunction, event) {
const value = event.currentTarget.value;
mySetFunction(value);
}
<TextField
placeholder="Email"
name="passwordResetEmailAddress"
onChange={(e) => handleTextFieldChange(setPasswordResetEmailAddress, e)}
>
{passwordResetEmailAddress}
</TextField>
I've tested it and it works.
class Yup extends React.Component {
state = {
first: "",
second: ""
};
handleTextFieldChange = ({ target: { name, value } }) =>
this.setState({ [name]: value });
render() {
const { first, second } = this.state;
return (
<div>
<p>{first}</p>
<p>{second}</p>
<input type="text" name="first" onChange={this.handleTextFieldChange} />
<input
type="text"
name="second"
onChange={this.handleTextFieldChange}
/>
</div>
);
}
}
same with hook
function Yup() {
const [{ first, second }, setState] = useState({ first: "", second: "" });
function handleTextFieldChange({ target: { name, value } }) {
setState(prevState => ({ ...prevState, [name]: value }));
}
return (
<div>
<p>{first}</p>
<p>{second}</p>
<input type="text" name="first" onChange={handleTextFieldChange} />
<input type="text" name="second" onChange={handleTextFieldChange} />
</div>
);
}
You can dynamically update a state for the target field by receiving an update state function as an argument in onChange function.
Example
import React, { useState } from "react";
const App = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const onChangeHandler = (setIdentifierState, event) => {
setIdentifierState(event.target.value);
};
return (
<div>
<p>{"Email: " + email}</p>
<p>{"Password: " + password}</p>
<input
type="text"
placeholder="email"
onChange={onChangeHandler.bind(null, setEmail)}
/>
<input
type="text"
placeholder="password"
onChange={onChangeHandler.bind(null, setPassword)}
/>
</div>
);
};
export default App;
I recently tackled this same problem while converting my components from classes to functions. I ended up creating an object that could then point to the separate state hooks:
const textStates = {};
const setTextStates= {};
for (var name of names) {
let [textState, setTextState] = useState("");
textStates[name] = textState;
setTextStates[name] = setTextState;
}
I solved it this way (a slightly more dynamic solution than what #VikR offered)
const [title, setTitle] = useState("");
const [desc, setDesc] = useState("");
const [video, setVideo] = useState("");
const handleChange = setter => event => {
const value = event.target.value;
//special cases
if (setter === setVideo) {
setInvalidVideo(!ReactPlayer.canPlay(value))
}
setter(value)
}
Then in my code:
<TextField fullWidth value={title} type="date"
label={t('service.ticket.add.title')}
placeholder={t('service.ticket.add.titlePh')}
onChange={handleChange(setTitle)} variant="outlined"/>