I'm creating a program to track store inventory. I have an array of item names (strings) that I map through to generate a component that renders a heading for each item along with a corresponding input field:
function Inventory(props){
let items = ['milk', 'bread', 'butter'],
itemInput = items.map((value,index) => {
return(
<div key={index}>
<h3>{value}</h3>
<input type={'text'} />
</div>
)
})
return(
<div>
{itemInput}
</div>
)
};
Screenshot of output
How can I access both the input value as well as well as its corresponding heading? For example, if I type 5
within the input for milk
, I want to be able to access both 5
and milk
.
I've tried using refs
(which winds up referencing only the last array item), event
and this
to no avail. Any suggestions will be much appreciated.
It is possible to use the onChange handler for this:
<input type="text" onChange={e => this.setState({ [value]: e.target.value })} />
The state now will look something like this:
{
milk: 5,
bread: 2,
butter: 10
}
You are using a functional
component which doesn't have a state
or refs
. You have two options, Either set the value as props passed down from the parent or make it a stateful
component.
Stateless
components must be dumb component used specifically for rendering and all logic must reside in the stateful parent component
.
According to the docs
You may not use the ref attribute on functional components because
they don't have instances.You should convert the component to a class
if you need a ref to it, just like you do when you need lifecycle
methods or state
In first case
function Inventory(props){
let items = ['milk', 'bread', 'butter'],
itemInput = items.map((val,index) => {
return(
<div key={index}>
<h3>{val}</h3>
<input type={'text'} value={props.childInput[val] || '' } onChange={(e) => props.handleChange(e, val)}/>
</div>
)
})
return(
<div>
{itemInput}
</div>
)
};
And then the parent will have the logic like
<Inventory handleChange={this.handleChange} childInput={this.state.childInputVal}/>
handleChange = (e, key) => {
var childInputVal = {...this.state.childInputVal}
childInputVal[key] = e.target.value
this.setState({childInputVal})
}
state = {
childInputVal: {}
}
The other option is to make this component itself a stateful component
class Inventory extends React.Component {
state= {
inputValues: {}
}
handleChange = (e, val) => {
handleChange = (e, key) => {
var childInputVal = {...this.state.inputValues}
inputValues[key] = e.target.value
this.setState({inputValues})
}
render() {
let items = ['milk', 'bread', 'butter'],
itemInput = items.map((val,index) => {
return(
<div key={index}>
<h3>{val}</h3>
<input type={'text'} value={this.state.inputValues[val] || '' } onChange={(e) => this.handleChange(e, val)}/>
</div>
)
}
return(
<div>
{itemInput}
</div>
)
}