I am stuck on this issue with onChange handler being fired on a wrong element after .map function. I have a component, which I use to display mapped values, which looks like this:
const Step: React.FC<StepProps> = ({ status, title, text, onClick, onChange }) => {
return (
<button disabled={status === CompletionStatus.Completed} className={Styles.item} onClick={onClick}>
<mui.IconButton css={css.completionIcon} disabled={status === CompletionStatus.Completed}>
{status === CompletionStatus.Completed ? <muiIcons.Check /> : <Plus />}
</mui.IconButton>
<div className={Styles.content}>
<span className={status === CompletionStatus.Completed ? Styles.titleCompleted : Styles.title}>{title}</span>
<span className={status === CompletionStatus.Completed ? Styles.textCompleted : Styles.text}>{text}</span>
</div>
{onChange && (
<>
<label htmlFor="file-button" className={Styles.inputLabel} />
<input id="file-button" className={Styles.input} type={'file'} onChange={onChange} />
</>
)}
</button>
);
};
So, some of the mapped elements are being used with onClick, and two use onChange to gain photos from the user.
The issue is, that every time I trigger the onChange event on any of those inputs, only the first ones fires, e.g (I added this onChange function to test the name of the element that is being fired, and every time only the first one in the list is being console.logged)
onChange={(event: any)=> {
console.log(event, step);
event.target.value = null;
}}
So, I have figured out the issue here, maybe someone finds this helpful.
Having input with type file only having one id (file-button) was causing only the first such input to work
<label htmlFor="file-button" className={Styles.inputLabel} />
<input id="file-button" className={Styles.input} type={'file'} onChange={onChange} />
The way I fixed this, was basically having that id property unique, so I passed an index to the component and changed the id to
id={`file-button-${index}`}
Sounds like you may not have set a key for each item in your mapping function.
{yourData.map((item, index) => <Component key={`item-${index}`} item={item} />)}
Related
I'm planning to set to autofocus the last element of the array by using a ref, but I don't know how to do it since I'm already using ref for react-hook-form
I've been to this link, but I don't know how to apply it in my case. How can I use multiple refs for an array of elements with hooks?,
Somebody knows how to inject this two refs to one input?
Here's my ref to auto focus an input
const [inputRef, setInputRef] = useFocus();
And here's my current codes with react-form-hook's ref:
{[...Array(10)].map((x, i) => (
<input
type="text"
name="book"
className="border mr-px-5"
ref={register(rules.book)}
ref={lastElement ? inputRef : null} // conditional focus ref
/>
)}
From frequent try and error, I finally found the solution. The refs can actually use as function and then set two refs in it.
And for the react hook form just pass the event on it to make it work also.
Here's the actual solution:
const [inputRef, setInputRef] = useFocus();
{[...Array(10)].map((x, i) => (
<input
type="text"
name="book"
className="border mr-px-5"
ref={(event) => {
register(event, rules.book);
if (lastElement) inputRef.current = event;
}}
/>
)}
Hello i have a filter function that's set in a variable, but want to execute as soon as the user clicks the button. The function enableFilter has to check if the item.id matches the item.category and fire the filter as it is true. Can anyone help me with that? It is not necessary to filter two at a time. Only one filter at a time
You can store your filter condition id in a state variable, like this:
const [selectedItemId, setSelectedItemId] = useState(null);
Then change your filter cases based on selectedItemId, like:
const filteredCases = i18n('casesOverview.cases').filter(category => selectedItemId ? category.category.includes(selectedItemId) : true)
then update the button onClick props with setSelectedItemId as follows:
{i18n('casesOverview.categories').map((item, key) => (
<ButtonOutline
onClick={() => setSelectedItemId(item.id)}
id={item.id}
key={key}>{item.label}</ButtonOutline>
))
}
<GridWideColumn>
{filteredCases.map((item, key) => (
<CardBrand
key={key}
text={item.teaser} href={item.link}
image={<Image src="" srcSet={item.coverImage} alt="" />}
brandImage={<Image src={item.clientLogo} alt="" />}
link={item.link}
client={item.client}
textInfo={item.category}
/>
))
}
I am displaying cards with data mapped with a price and location
return data.map( FD1 => (
<Row>
<Card className="card">
<Card body className="text-center">
<CardTitle data-es-label="location"> Location:
{FD1.Departure}
</CardTitle>
<CardText data-es-label="Price">Price
{FD1.price}
</CardText>
<label>
<Checkbox
id={FD1.FlightID}
name={FD1.FlightID}
checked={this.state.checked === FD1.FlightID}
onChange={this.handleCheckboxChange}/>
<span>Select</span>
</label>
<CardActions>'
Each card has a check box and my idea was when the check box is selected and submitted - the information mapped to that card will be sent to be 'booked'.
Is it possible to filter data from the mapped data. Each 'card' with the data has a unique id. How do I filter the data by card and send to a booking page with the details?
At the moment when I select a checked box they all select.
handleCheckboxChange = event =>
this.setState({ checked: event.target.checked });
EDIT: attempt - this is what I have tried
handleCheckboxChange = event =>
this.setState({ checked: event.target.checked });
Select(FD) {
this.state={checked:FD.FlightID};
return(
<label>
<Checkbox id={FD.FlightID}
name={FD.FlightID}
checked={this.state.checked}
onChange={this.handleCheckboxChange}
/>
<span>Select</span>
</label>
)
}
Do you know where I have gone wrong?
into the checked state save the FD1.FlightID instead of true/false and you will know exactly which card had been checked.
and then on submit you can send the appropriate data using this.state.checked (the id of checked card)
In the code inside your "map" function, "this" is still referring to the parent component. So you only have one checked state. That's why all your checkboxes show the same behaviour.
A cleaner solution would be to define a new stateful component "CardRow" that has everything inside the map plus it's own state and handleCheckboxChange function.
Alrighty, so I'm using some color picker that changes my this.state.colorPicked and that works fine, now I have an input field I'd like to write a function that I can call that would concat the value of my state colorPicked if it was empty, or concat comma and that value if it was not, how can I access this input field?
I'm using react-bootstrap
<InputGroup className="mb-3">
<FormControl
name="color"
onChange={this.change}
placeholder="Available Colors"
aria-label="Available Colors"
/>
</InputGroup>
<div style={{ display: "flex", "flex-direction": "row" }}>
<TwitterPicker
className="mb-3"
onChange={this.handleColorChange}
/>
<Button
style={{
background: this.state.colorPicked
}}
onClick={() => this.addColor(this.state.colorPicked)}
>
Add Color!
</Button>
</div>
TIA
Clarification: I want to pick the color, the onchange detector sets it to the state and hence the button's background, now on button click I want to add it's hex (the state) to the text field as text input, if input is not empty I would precede that with a comma.
With react forms we use 2 way binding from state, onChange and value attributes.
First add the value attribute and set it to state property for ex inputField.
<FormControl
name="color"
onChange={this.change}
placeholder="Available Colors"
aria-label="Available Colors"
value=={this.state.inputField}
/>
Now you can write a logic inside your change function.. something like
change = (e)=>{
this.setState({inputField:e.target.value}
// then copy array from state
let arrFromState = [...this.state.colorPicked]
let updatedArray = [...arrFromState, this.state.inputField]
// or use concat, push etc
this.setState({colorPicked:updatedArray})
}
I didn't understood the exact thing you wanted to do.
But thats the basic approach while working with react forms. Two way binding
To access input value you can use onChange method on your input, pass it an event and access it through event.target.value. It should look similar to this:
<input onChange={(event) => logInputValue(event)}
const logInputValue = ({ event: { target: { value } = {} } }) => { console.log(value) }
I have a string , in certain places I need to insert input tags with values. Everything displays fine , but I can't delete or edit values in input. What is wrong with that input?
editModalText() {
let modalMessage="Hello, my name is /# Ann #/. I'm working for /# IStaff #/, could you please call me back"
return (
<div>
{modalMessage
.split("/")
.map((text, idx) =>
text.includes("#") ? this.replaceCharacter(idx, text) : text,
)}
</div>
)
}
replaceCharacter(idx, text) {
let formattedText = text.replace(/#/g, " ")
return (
<input
key={idx}
value={formattedText}
onChange={e => this.setState({input:e.target.value})}
/>
)
}
replace value={formattedText} with defaultValue={formattedText}
this way input will be editable. it will show default value on first render and as you type you'll store that value in your state.
you can read more about controlled and uncontrolled components in the docs
I think you need to bind the input value and the state together. I am not sure how you're currently calling replaceCharacter but I would do something like this :
replaceCharacter(idx) {
return (
<input
key={idx}
value={this.state.input.replace(/#/g, " ")}
onChange={e => this.setState({input:e.target.value})}
/>
)
}
This way when you update your state with the onChange event the value of the state will be populated in the input.