Gutaozk
Gutaozk

Reputation: 3

React - change defaultValue input inside a component

If I change the input value and click the copy-button(formItem.jsx), the first time the card is duplicated perfectly, but when I change the input value again and click the button-copy the new card has the first one value First value ==> Cloning First value ==> Second value ==> Cloning Second value

If I use value and onChange instead of defaultValue and onBlur the card is duplicated perfectly, but in my project i will need many cards, therefore i will need many input fields, however using value and onChange if I have many inputs, when typing, there is a lag of up to several seconds for the new text to display in the field.

What can I do?

import "./header.css"
import React, { useState } from 'react';
import FormItem from './formItem'

export default (props) =>  {

const cardItemDefault = {
    skin: '',
}

const [cardsItems, setCardsItems] = useState([])

const handleAddCardItem = () => {
    setCardsItems([...cardsItems, cardItemDefault])
}
const handleRemoveCardItem = (index) => {
    const values = [...cardsItems]
    values.splice(index, 1)
    setCardsItems([...values])
}

const handleChangeInput = (index, event) => {
    const values = [...cardsItems]
    values[index].skin = event.target.value
    setCardsItems([...values])
}
const handleAddCopyCard = (index) => {
    const values = [...cardsItems]

    const copyCard = {
        skin: values[index].skin,
    }
    
    values.splice(index+1, 0, copyCard)
    setCardsItems([...values])
    
}
return (
    <>  
        <nav className="NavbarItems">
            <div className="marca">
                <div className="menu-icon">
                    <img src="https://cdn.discordapp.com/attachments/825108658622955520/834516008982151178/image0.png" alt="catIcon"/>
                </div>
                <h1 className="navbar-logo">My project</h1>
            </div>            
            <ul className="nav-menu">
                <li>
                    <button className="button-addformItem" onClick={() => handleAddCardItem()}>Add</button>
                </li>
            </ul>
        </nav>
        <div className="container">
            <div className="cards-items">
                {cardsItems.map( (cardItem, index) => {
                    return (
                        <FormItem
                            key={index} 
                            idItem={index}
                            skinCardItem={cardItem.skin}
                            onHandleRemoveCardItem={() => handleRemoveCardItem(index)}
                            onHandleChangeInput={(event) => handleChangeInput(index, event)}
                            onHandleClickCopy={() => handleAddCopyCard(index)}
                        />
                    )
                })}
            </div>
        </div>    
    </>
)

}

formItem.jsx

import "./formItem.css"
import React from 'react'

export default (props) => {

return (
    <div className="card-formItem">
        <div className="container-formItem">
            <div className="header-formItem">
                <i 
                    className="fas fa-times fa-2x remove-item-button"
                    id={"remove-item-button" + props.idItem}
                    title='Remover item' 
                    onClick={props.onHandleRemoveCardItem}
                />
            </div>

            <div className="formItem">
                <div className="skin-input">
                    <input name="skin"
                        type="text" 
                        placeholder="Skin"
                        defaultValue={props.skinCardItem}
                        onBlur={(event) => props.onHandleChangeInput(event)}
    
                    />
                </div>
            </div>  

        </div>
        <i 
            className="far fa-copy fa-2x copy-button"
            id={"copy-button" + props.idItem}
            onClick={() => props.onHandleClickCopy()}>
        </i>
    </div>
)
}

Upvotes: 0

Views: 195

Answers (1)

Ryan Wheale
Ryan Wheale

Reputation: 28400

The main problem is that the state for all the cards is stored in one place in the parent component. This means that any time any input changes, ALL of the cards are re-rendered. You are going to need to drastically rethink how you store state here. I'm not going to write the code for you, but here's what you need to do:

  • Move the value and onChange stuff into the FormItem component. Each form item should be responsible for its own value and event handlers.
  • Create a "form context" to wrap around all the form items (read about react context here). You should be able to save all of the values to this context, as well as any helper functions for cloning and such. This will be useful for both cloning items and collecting all the values for all the inputs.
  • Find a better way to assign "keys" to your form items. The same key should be assigned to the same form item even when new form items are added. Using the index is not good enough. Imagine you have 3 form items and you clone the middle one, the last form item will get a new key when it really shouldn't. You need to generate a "id" for each form item and make sure that ID follows that form item even when new ones are added.

Good luck.

Upvotes: 0

Related Questions