TransWikia.com

Problemas creando un id para objeto javascript

Stack Overflow en español Asked on November 1, 2020

estoy haciendo una pequeña funcionalidad en react que desde un componente se dispara una funcion addProduct(item, type) el argumento item representa el objeto (ejemplo):

{product_name: 'can', product_price: 1200} 

Y ese es el que pasa como item a la funcion arriba nombrada, luego esta funcion (dependiendo del type) agrega al state el item(objeto de arriba) a un array llamado:

canArray: []

Lo que me paso luego es el problema, cuando quiero borrar el ultimo de la lista tuve que añadirle un id al objeto dentro de la funcion addProduct que se iba incrementando al click pero si bien, agrega el objeto con el nuevo atributo id a medida que se agrega reemplaza en todos los atributos id de los objetos de el array (arriba mencionado) el nuevo valor.

Cabe señalar que el id lo iba creando en referencia al length del array canArray, intente hacer una validacion para que no tuviese ese comportamiento pero sin exito por lo que me es imposible hacer un filter ya que me repite los mismos id(del ultimo que se le agrego al objeto).

Adjunto el context que contiene la funcion:

    class AppProvider extends React.Component {
        constructor(props) {
            super(props);
            this.state = {

                loginCredentials: {},
                signUpCredentials: {},
                isLoadingAuth: false,
                isUserAuthenticated: false,
                canArray: [],
                quantity: null,

                actions: {
                    loginInputData: this.loginInputData,
                    signIn: this.signIn,
                    signUpInputData: this.signUpInputData,
                    signUp: this.signUp,
                    addProduct: this.addProduct,
                    removeProduct: this.removeProduct
                }
            }
        }

        loginInputData = (text, name) => {
            const { loginCredentials } = this.state;
            const loginData = { [name]: text }
            const newData = {...loginCredentials, ...loginData};
            this.setState({
                loginCredentials: newData
            });
        }

        signIn = async(e) => {
        const { loginCredentials } = this.state;

        this.setState({
            isLoadingAuth: true
        });

        const { signInUser } = this.props;
        try {
            const resp = await signInUser({variables: {
                    email: loginCredentials.email,
                    password: loginCredentials.password
            }});

            this.setState({
                isUserAuthenticated: true,
                isLoadingAuth: false
            });
            this.storeData(resp.data.signInUser.token);
        } catch(err) {
            console.log(err);
        }
        }

        signUpInputData = (text, name) => {
            const { signUpCredentials } = this.state;
            const signUpData = { [name]: text }
            const newData = {...signUpCredentials, ...signUpData}
            this.setState({
                signUpCredentials: newData
            });
        }

        signUp = async() => {
            const { signUpCredentials, isUserAuthenticated } = this.state;
            
            this.setState({
                isLoadingAuth: true
            });

            const { createUser } = this.props;
            try {
                const resp = await createUser({variables: {
                    name: signUpCredentials.name,
                    email: signUpCredentials.email,
                    password: signUpCredentials.password
                }});
                this.setState({
                    isUserAuthenticated: true,
                    isLoadingAuth: false
                });
                await AsyncStorage.setItem('isUserAuth', toString(isUserAuthenticated));
            } catch(err) {
                console.log('err ', err);
            }
        }

        addProduct = (item, type) => {
            const { canArray } = this.state;
            if(type === 'canType') {
                item.id = canArray.length
                this.setState({
                    canArray: canArray.concat(item)
                })
            } else {
                return true;
            }

        }
        

A continuacion el componente que consume las funciones de context:

    const CAN_BOTTLE = { product_name: 'can', product_price: 1800 }

    class CanProduct extends React.Component {

    itemQuantity = () => {
        return(
            <Context.Consumer>
                {co => {
                    console.log(co.canArray)
                    return(
                            <View>
                                <Text style={styles.number}>{co.canArray.length > 0 ? co.canArray.length : 0}</Text>
                            </View>
                    )
                }}
            </Context.Consumer>
        )
    }
    
    render() {

        return(
            <Context.Consumer>
                {co => {
                    return(
                    <View style={styles.productContainer}>
                        <View style={styles.productSelectionContainer}>
                            <Image source={can} style={styles.productImage} />
                            {this.itemQuantity()}
                            <View style={styles.productTextContainer}>
                                <Text style={styles.productTitle}>Can Bottles</Text>
                                <Text style={styles.productDesc}>Make sure the bottles are empty</Text>
                            </View>
                            <View style={styles.buttonAddContainer}>
                                <TouchableOpacity onPress={e => co.actions.addProduct(CAN_BOTTLE, "canType")}>
                                    <Image source={addMore} />
                                </TouchableOpacity>
                                <TouchableOpacity onPress={e => co.actions.removeProduct("canType")}>
                                    <Image source={less} style={styles.lessButton}/>
                                </TouchableOpacity>
                            </View>
                        </View>
                    </View>
                    )
                }}
            </Context.Consumer>
        );
    }
    }

One Answer

splice es un método que altera (muta) el array original. Estás removiendo un item del array, y luego estás invocando setState, pero el arreglo ya fue modificado. Esto produce inconsistencias entre el "estado" y la interfaz de usuario.

Se recomienda evitar este tipo de métodos porque producen estos efectos inesperados en la aplicación. Utiliza siempre setState para mantener el "estado" de la aplicación en sincronía con la interfaz de usuario.

Si necesitas remover un elemento de un arreglo, puedes usar filter ya que no modifica el arreglo original.

Para agregar algo al array, puedes usar Sintáxis Spread para "abrir" el arreglo existente, y luego agregar el nuevo elemento.

Ejemplo completo:

class App extends React.Component {
  state = {
    products: [
      { id: 1, product_name: "soda", product_price: 1800 },
      { id: 2, product_name: "rice", product_price: 400 },
      { id: 3, product_name: "can", product_price: 2000 }
    ]
  };
  inputName = React.createRef();
  inputPrice = React.createRef();

  handleRemove = (id) => {
    this.setState((prevState) => {
      return {
        products: prevState.products.filter((p, i) => p.id !== id)
      };
    });
  };

  handleAdd = (name = "apples", price = 300) => {
    this.setState((prevState) => {
      const lastItem = prevState.products[prevState.products.length - 1];
      return {
        products: [
          ...prevState.products,
          { id: lastItem.id + 1, product_name: name, product_price: price }
        ]
      };
    });
  };

  render() {
    const { products } = this.state;

    return (
      <div>
        <ul>
          {products.map((p, i) => (
            <li key={p.id}>
              id: {p.id} / {p.product_name} / {p.product_price}
              <button onClick={() => this.handleRemove(p.id)}>Remover</button>
            </li>
          ))}
        </ul>
        <label>
          Nombre:
          <input ref={this.inputName} type="text" />
        </label>
        <br />
        <label>
          Precio
          <input ref={this.inputPrice} type="number" />
        </label>
        <br />
        <button
          onClick={() =>
            this.handleAdd(
              this.inputName.current.value,
              this.inputPrice.current.value
            )
          }
        >
          Agregar
        </button>
      </div>
    );
  }
}

ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Correct answer by Juan Marco on November 1, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP