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 displaying a list. Each item in the list is having a textbox. Textbox is showing DisplayOrder. Please find the sandbox: https://codesandbox.io/s/solitary-butterfly-4tg2w0
In Post API call, how to pass changed textbox values with corresponding description-id as a collection. StoredProcedure is taking description-id and display-sequence as parameters to save changed data in the database.
Please help on form submit how to do this? Thanks
import "./styles.css";
import React from "react";
import XMLParser from "react-xml-parser";
const data = `<?xml version="1.0"?>
<Category>
<description description-id="11" display-sequence="2">testing</description>
<description description-id="15" display-sequence="5">Guide</description>
<description description-id="20" display-sequence="7">test</description>
<description description-id="25" display-sequence="10">Guide</description>
<description description-id="30" display-sequence="12">test</description>
</Category>
</xml>`;
const REQUEST_URL = "";
const axios = {
get: () =>
new Promise((resolve) => {
setTimeout(resolve, 1000, { data });
})
};
class Sort_Descr extends React.Component {
constructor(props) {
super(props);
this.state = {
proddescriptions: [],
proddescription_id: "",
display_sequence: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {}
componentDidMount() {
this.getlistofdescriptions();
}
getlistofdescriptions() {
axios
.get(REQUEST_URL, { "Content-Type": "application/xml; charset=utf-8" })
.then((response) => {
const jsonDataFromXml = new XMLParser().parseFromString(data);
const descriptions = jsonDataFromXml.getElementsByTagName(
"description"
);
console.log(descriptions);
this.setState({
proddescriptions: jsonDataFromXml.getElementsByTagName("description")
});
});
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<div>
<ul style={{ listStyle: "none" }}>
{this.state.proddescriptions.map((item, index) => {
return (
<li key={item.attributes["description-id"]}>
{item.attributes["description-id"]}
<input
type="text"
size="5"
maxlength="3"
value={item.attributes["display-sequence"]}
onChange={this.handleChange}
/>
{item.value}
</li>
);
})}
</ul>
</div>
<input type="submit" name="submit" value="Submit" id="btnsubmit" />
</form>
</div>
);
}
}
export default function App() {
return (
<div className="App">
<h4>Sort list by updating the number in the textbox</h4>
<Sort_Descr />
</div>
);
}
Instead of storing and updating JSON/XML data in state I'd suggest mapping it to a simpler object that is easier to identify and update. React state should be the minimal amount of data necessary to represent your information, store only what you need.
const proddescriptions = descriptions.map(({ attributes, value }) => ({
id: attributes["description-id"],
sequence: attributes["display-sequence"],
value
}));
this.setState({ proddescriptions });
When mapping the state.proddescriptions to the inputs, use the id as the inputs name attribute value for identification when updating.
{this.state.proddescriptions.map((item) => (
<li key={item.id}>
<label>
{item.id}
<input
type="text"
name={item.id} // <-- name attribute
size="5"
maxLength="3"
value={item.sequence}
onChange={this.handleChange}
/>
</label>
{item.value}
</li>
))}
Implement the handleChange to shallow copy the previous state and update the matching array element by id.
handleChange = (event) => {
const { name, value } = event.target;
this.setState((prevState) => ({
proddescriptions: prevState.proddescriptions.map((el) =>
el.id === name
? {
...el,
sequence: value
}
: el
)
}));
};
In the handleSubmit callback use the onSubmit event object to prevent the default form action and access the current state.proddescriptions and map it back to any format your APIs are expecting.
handleSubmit = (event) => {
event.preventDefault();
console.log(this.state.proddescriptions);
// manipulate `state.proddescriptions` into a request payload
};
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>
I have made a form in react which takes input from the user and stores it in the state. Now, I want to display the state values when the user clicks Submit button in an input field just below the submit button in React.
Im new to react.
You have to make an object (E.g. Credentials) and when someone clicks the button, credential takes the props of the state like this:
App.js
//import code....
import Form from './Form.js'
//class app code.....
//in the render method:
render() {
return (
<Form />
)
}
Form.js
// import code ....
state = {
firstName: '', // or what you want
lastName: '', // or what you want
email: '', // or what you want
send: false,
}
//handleChange function
const handleChange = (event) => {
const {name, value} = event.target
this.setState({
[name]: value
})
}
//handleClick function
const handleClick = () => {
this.setState({send: true})
}
In the Render method
render() {
return (
<div>
<input name='firstName' onChange={handleChange} />
<input name='lastName' onChange={handleChange} />
<input name='email' onChange={handleChange}/>
<button onClick={handleClick}>Send</button>
{send &&
<Credentials
firstName={this.state.firstName}
lastName={this.state.lastName}
email={this.state.email}
/>
}
</div>
)
}
export default Form // or your file's name
In the Credential.js
//import code...
const Credentials = ({firstName, lastName, email}) => {
return (
<h2>firstName is: {firstName}</h2>
<h4>lastName is: {lastName}</h4>
<p>email is: {email}</p>
)
}
export default Credentials
In React you can use 'useState' to initiate a number or any kind of input. Then set that number when the user clicks on a button.
import React, { useState } from "react";
function App() {
const [number, setNumber] = useState();
let typedNumber = 0;
const btnHandler = (e) => {
e.preventDefault();
setNumber(typedNumber);
};
return (
<div>
<form onSubmit={btnHandler}>
<input type="text" onChange={(e) => (typedNumber = e.target.value)} />
<input type="submit" />
</form>
<p>{number}</p>
</div>
);
}
export default App;
Novice.
I have a class Address which I ultimately want to split into a presentational component and container. It all works as is but when I move this particular function outside the render function from initially within the actual async.select form field -
onSuburbChange = (value) => {
this.setState({ selectedSuburb: value }, () => {
input.onChange(value)
updatePostcodeValue(value ? value.postcode : null, sectionPrefix)
})
}
...I find I am getting hit with a number of errors based on the the fact that they are unreferenced.
The error I get is
address.jsx:56 Uncaught ReferenceError: input is not defined
If I comment this line out I get the same type of error on updatePostcodeValue.
Here is the entire address file. As you can see it would be good to move the presentational section in render off to another file but I need to move all the functions to the outside of the render function.
NOTE: I have commented out where the function orginal sat so anybody who has a crack at this question knows where it was and also where I intended to move it...
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { Field, change } from 'redux-form'
import { Col, Panel, Row } from 'react-bootstrap'
import Select from 'react-select'
import { getSuburbs } from './actions'
import FormField from '../formComponents/formField'
import TextField from '../formComponents/textField'
import StaticText from '../formComponents/staticText'
import { CLIENT_FORM_NAME } from '../clients/client/client'
export class Address extends Component {
static contextTypes = {
_reduxForm: PropTypes.object.isRequired,
}
constructor(props, context) {
super(props, context)
this.state = {
selectedSuburb: null,
}
}
// Manage Select for new data request - for suburbs.
handleSuburbSearch = (query) => {
console.group('handleSuburbSearch')
console.log('query', query)
const { addressData } = this.props
console.log('addressData', addressData)
const companyStateId = addressData.companyStateId
console.log('companyStateId', companyStateId)
if (!query || query.trim().length < 2) {
console.log('no query; bailing!')
console.groupEnd()
return Promise.resolve({ options: [] })
}
const queryString = {
query: query,
companyStateId: companyStateId,
}
console.log('queryString', queryString)
return getSuburbs(queryString)
.then(data => {
console.log('Suburbs returned!', data)
console.groupEnd()
return { options: data }
})
}
//I HAVE MOVED IT TO HERE....
onSuburbChange = (value) => {
this.setState({ selectedSuburb: value }, () => {
input.onChange(value)
updatePostcodeValue(value ? value.postcode : null, sectionPrefix)
})
}
render() {
const { addressData, updatePostcodeValue } = this.props
const { value } = this.state
const sectionPrefix = this.context._reduxForm.sectionPrefix
return (
<Panel header={<h3>Client - Address Details</h3>}>
<Row>
<Field component={TextField}
name="address1"
id="address1"
type="text"
label="Address Line 1"
placeholder="Enter street 1st line..."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
<Field component={TextField}
name="address2"
id="address2"
type="text"
label="Address Line 2"
placeholder="Enter street 2nd line..."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
</Row>
<Row>
<Field
component={props => {
const { input, id, placeholder, type } = props
const { fieldCols, labelCols, controlCols, label, inputClass } = props
// just the props we want the inner Typeahead textbox to have
const { name, onChange } = input
const onStateChange = (state) => {
console.log('onStateChange', state)
onChange(state)
}
return (
<FormField
id={id}
label={label}
fieldCols={fieldCols}
labelCols={labelCols}
controlCols={controlCols}
inputClass={inputClass}
>
<Select
name={name}
onChange={onStateChange}
placeholder="Select state"
valueKey="id"
options={addressData.states}
labelKey="stateLabel"
optionRenderer={option => `${option.stateShortName} (${option.stateName})`}
value={input.value}
selectValue={Array.isArray(input.value) ? input.value : undefined}
/>
</FormField>
)
}}
name="state"
id="state"
label="State."
fieldCols={6}
labelCols={3}
controlCols={6}
/>
</Row>
<Row>
<Field
component={props => {
const { input, id, placeholder, type } = props
const { fieldCols, labelCols, controlCols, label, inputClass } = props
const { name, value, onChange, onBlur, onFocus } = input
const inputProps = {
name,
value,
onChange,
onBlur,
onFocus,
}
{/*onSuburbChange = (value) => {
this.setState({ selectedSuburb: value }, () => {
input.onChange(value)
updatePostcodeValue(value ? value.postcode : null, sectionPrefix)
})
}*/}
return (
<FormField
id={id}
label={label}
fieldCols={fieldCols}
labelCols={labelCols}
controlCols={controlCols}
inputClass={inputClass}
>
<Select.Async
{...inputProps}
onChange={this.onSuburbChange}
valueKey="id"
labelKey="suburbName"
loadOptions={this.handleSuburbSearch}
backspaceRemoves={true}
/>
</FormField>
)
}}
name="suburb"
id="AddressLocation"
label="Suburb."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
</Row>
<Row>
<Field component={StaticText}
name="postcode"
id="postcode"
label="Postcode."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
</Row>
</Panel>
)
}
}
const AddressContainer = connect(state => ({
addressData: state.addressData,
}), dispatch => ({
updatePostcodeValue: (postcode, sectionPrefix) => dispatch(change(CLIENT_FORM_NAME, `${sectionPrefix ? (sectionPrefix + '.') : ''}postcode`, postcode))
}))(Address)
export default AddressContainer
How do I structure the onSuburbChange so that it can sit outside the render function, update the onChange value and also update the Postcode etc?
well, if you look at the method, you'll see that... well, input is undefined in that scope.
onSuburbChange = (value) => { // <-- scope starts here
this.setState({ selectedSuburb: value }, () => {
input.onChange(value) // <-- input used here
updatePostcodeValue(value ? value.postcode : null, sectionPrefix)
})
}
assuming Select.Async is a "magic" blackbox Component that you don't have access to/are able to change, and the only parameter you get back from it in the callback is the new value, your best bet is a ref on the input.
<Field ref={(input) => this.input = input } ... />
and then change it to this.input instead of just input
I think you could also partially apply it (it's late any I'm not thinking straight) - it would look like
onSuburbChange = (input, value) => {
this.setState({ selectedSuburb: value }, () => {
input.onChange(value)
updatePostcodeValue(value ? value.postcode : null, sectionPrefix)
})
}
--
const mOnChange = onSuburbChange.bind(null, input) while input is in scope.
updatePostcodeValue can be referenced from props in the callback - and you've already taken care of ensuring it has the correct scope by using ES6 arrow function notation. Just destructure it out of props just like you did in render at the top of the callback.
also, unrelated, but you REALLY oughta break out those component props into another file or function...