简体   繁体   中英

reactjs custom component input value doesnt update with state change

I am working on a list app, and I am having issues with the components not updating correctly. I pull the users list from a JSON file and save it in a state. I am using context to pass that state and other information around to my different compoents. The smallest component is the user items broken out into a list which is editable. It is here with the list of items that I am having issues with.

For example, I have two different JSON files:

    [{"userId": 81944,
        "listId": 1,
        "title": "testa",
        "items": [
            {
                "listItemId": 0,
                "product": "walnuts",
                "quantity": 1,
                "category": "Bakery",
                "unit": "Each",
                "cart": false
            },
            {
                "listItemId": 1,
                "product": "syrup",
                "quantity": 1,
                "category": "Beverages",
                "unit": "Each",
                "cart": true
            },
            {
                "listItemId": 2,
                "product": "cinnamon",
                "quantity": 6,
                "category": "Bakery",
                "unit": "Each",
                "cart": false
            },
            {
                "listItemId": 3,
                "product": "gabonzo beans",
                "quantity": 1,
                "category": "Canned Goods",
                "unit": "Each",
                "cart": true
            },
            {
                "listItemId": 4,
                "product": "diced tomatos",
                "quantity": 7,
                "category": "Produce",
                "unit": "Each",
                "cart": false
            },
            {
                "listItemId": 5,
                "product": "milk",
                "quantity": 1,
                "category": "Dairy",
                "unit": "Oz",
                "cart": false
            },
            {
                "listItemId": 6,
                "product": "salmon",
                "quantity": 3,
                "category": "Meat",
                "unit": "Lb",
                "cart": false
            }]},{
        "userId": 78863,
        "listId": 4,
        "title": "testd",
        "items": [
            {
                "listItemId": 0,
                "product": "half and half",
                "quantity": 1,
                "category": "Dairy",
                "unit": "Each",
                "cart": false
            },
            {
                "listItemId": 1,
                "product": "Blue Cheese",
                "quantity": 1,
                "category": "Dairy",
                "unit": "Each",
                "cart": false
            },
            {
                "listItemId": 2,
                "product": "Garlic",
                "quantity": 1,
                "category": "Produce",
                "unit": "Each",
                "cart": false
            },
            {
                "listItemId": 3,
                "product": "Chestnuts",
                "quantity": 1,
                "category": "Other",
                "unit": "Each",
                "cart": false
            },
            {
                "listItemId": 4,
                "product": "Balsamic Vinegar",
                "quantity": 1,
                "category": "Other",
                "unit": "Each",
                "cart": false
            },
            {
                "listItemId": 5,
                "product": "Onions",
                "quantity": 1,
                "category": "Produce",
                "unit": "Each",
                "cart": false
            },
            {
                "listItemId": 6,
                "product": "Flax Seed",
                "quantity": 1,
                "category": "others",
                "unit": "Each",
                "cart": false
            },
            {
                "listItemId": 7,
                "product": "Plantains",
                "quantity": 1,
                "category": "Produce",
                "unit": "Each",
                "cart": false
            }]}]

In my app I have a dialog box that allows me to switch between my lists. I then take list and pass it a custom component to be drawn on the screen.

import React, {useState,useEffect} from 'react';



const Card=(props)=>{
    //console.log('prop');
    const [cart, setCart] = useState(props.cart);
    const [Product, setProduct] = useState(props.item);
    const [Quantity, setQuantity] = useState(props.units);

    // useEffect(()=>{
    //  setProduct(props.item)
    //  setQuantity(props.units)
    //  setCart(props.cart);

    // },[])
    console.log(props)
    return (
        <li key={props.value}>
            <div>
                <input type="checkbox" checked={cart} onChange={(e)=>{props.cartChange(e.target)}}/>
            </div>
            <div>
                <input id={'product '+props.value} className='update' 
                type='text' value={Product} 
                onChange={(e)=>setProduct(e.target.value)}
                 />
                <br/>
                <input id='quantityValue' className='update' 
                type='number' value={Quantity} 
                onChange={(e)=>setQuantity(e.target.value)}
                 />
                <span id='quantityType' className='update'>{props.unitType}</span>
            </div>
            <div>
                <button id='save-button' type='button' 
                onClick={(e)=>{props.change(Product,Quantity,props.value)}}>&#10003; save</button>
                <button id='delete-button' type='button'>&#10007; delete</button>
            </div>
        </li>
    )
}
export default Card;

This is the code that calls the custom components. You will see that I am calling it from a array.map() those arrays are fine, and have the correct information in them.

import React, {useContext,useEffect} from 'react';
import {DataContext} from '../../../context/test/DataContext'
import Card from './ItemCard';

const update=(x)=>{
    console.log(x)
}

const List = () =>{
    const {listId} = useContext(DataContext);
    const {userItemList} = useContext(DataContext);
    const {GetItemList} = useContext(DataContext);
    const {ListSplit} = useContext(DataContext);
    const {foundList} = useContext(DataContext);
    const {findList} = useContext(DataContext);
    const {Updater} = useContext(DataContext);
    const {cartUpdater} = useContext(DataContext);
    useEffect(()=>{
        GetItemList();
    },[listId])
    useEffect(()=>{
        ListSplit();
    },[userItemList])
    // console.log(findList);
    // console.log(foundList);

    return(
        <div>
            <p>To find:</p>
            <ul>
            {findList.map((item,index)=><Card key={item.listItemId} index={index}
                value={item.listItemId} cart={item.cart} item={item.product} 
                units={item.quantity} unitType={item.unit} 
                cartChange={cartUpdater} change={Updater} />)}
            </ul>
            <p>Found:</p>
            <ul>
            {foundList.map((item,index)=><Card key={item.listItemId} index={index}
                value={item.listItemId} cart={item.cart} item={item.product} 
                units={item.quantity} unitType={item.unit} 
                cartChange={cartUpdater} change={Updater} />)}
            </ul>
        </div>
    )
}
export default List;

Each time I switch this, the props that I console log out change correctly. Also, if I look at my compoents in dev tools (chrome) I see that the states should be correct, however what I see on the screen is not correct. For example the second item which is cinnamon, if I switch to the second list should be Blue Cheese. The prop changes, as does the state, but what I see on the screen is still cinnamon.

I know that I probably didnt explain it that clearly, but below is a screen shot of what I am talking about.

在此处输入图片说明

You were close with the commented out code. Since you are setting your props to state (which is a bad idea and I will discuss at the bottom), your useState only sets the state initially. You want to watch these props and update when they do.

useEffect(() => {
 setProduct(props.item)
 setQuantity(props.units)
 setCart(props.cart);

}, [props.item, props.units, props.cart]);

The items in the array are what useEffect watches to know if it should fire.

As a side note - assigning props to state is a bad idea and you've seen why - they don't automatically update. You should hoist up where these props are set to the parent and you can pass them down as props and use them directly. You can pass down the setters as props as well, which can update the parent.

This article , while referencing class based React component may provide more information if you'd care to read up on it.

 const { useState } = React; const initialItems = [ { listItemId: 1, product: 'syrup', quantity: 1, category: 'Beverages', unit: 'Each', cart: true, }, { listItemId: 2, product: 'cinnamon', quantity: 6, category: 'Bakery', unit: 'Each', cart: false, }, { listItemId: 3, product: 'garbanzo beans', quantity: 1, category: 'Canned Goods', unit: 'Each', cart: true, }, ]; const Parent = () => { const [items, setItems] = useState(initialItems); const updateProduct = listItemId => (e) => { setItems(items.map((item) => { if (item.listItemId === listItemId) { return { ...item, product: e.target.value }; } return item; })); }; const updateQuantity = listItemId => (e) => { setItems(items.map((item) => { if (item.listItemId === listItemId) { return { ...item, quantity: e.target.value }; } return item; })); }; return ( <div> <div style={{ width: "50%" }}> All Items - (this state lives inside the parent component) </div> <div> {items.map(item => ( <div> <div> Product - {item.product} </div> <div> Quantity - {item.quantity} </div> </div> ))} </div> <div style={{ width: "50%" }}> {items.map(item => ( <Child item={item} updateQuantity={updateQuantity} updateProduct={updateProduct} /> ))} </div> </div> ); }; const Child = ({ item, updateQuantity, updateProduct }) => { return ( <div> <div> <span> Product - </span> <span> <input value={item.product} onChange={updateProduct(item.listItemId)} /> </span> </div> <div> <span> Quantity - </span> <span> <input value={item.quantity} onChange={updateQuantity(item.listItemId)} /> </span> </div> </div> ); }; ReactDOM.render( <Parent />, 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>

A little example above. The parent component holds the state of the items and maps over each to create a child component. This example is a little rough around the edges. You could do something like adding data-id and name to each input to simplify the updating functions, or use useReducer to hoist that logic a bit, but I think it gets you going in the right direction.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM