Гаки React useState () з Object


103

Який правильний спосіб оновлення стану - це вкладений об’єкт у React with Hooks?

export Example = () => {
  const [exampleState, setExampleState] = useState(
  {masterField: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b"
           fieldTwoTwo: "c"
           }
        }
   })

Як можна використовувати setExampleStateоновлення exampleStateдо a(додавання поля)?

const a = {
masterField: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b",
           fieldTwoTwo: "c"
           }
        },
  masterField2: {
        fieldOne: "c",
        fieldTwo: {
           fieldTwoOne: "d",
           fieldTwoTwo: "e"
           }
        },
   }
}

b (Зміна значень)?

const b = {masterField: {
        fieldOne: "e",
        fieldTwo: {
           fieldTwoOne: "f"
           fieldTwoTwo: "g"
           }
        }
   })


ви маєте на увазі додавання нового значення ключа об’єкта до існуючого об’єкта?
Просто

@Justcode Для першого прикладу так, для другого прикладу просто зміни існуючого об'єкта
isaacsultan

Відповіді:


109

Ви можете передати нове значення таким чином

  setExampleState({...exampleState,  masterField2: {
        fieldOne: "c",
        fieldTwo: {
           fieldTwoOne: "d",
           fieldTwoTwo: "e"
           }
        },
   }})

2
Чудово, це відповідає частина а! чи знаєте ви найкращу практику для частини b?
isaacsultan

1
змінюється також те ж саме минуле masterFieldзамість masterField2 setExampleState({...exampleState, masterField:{// нові значення}
асеферов

3
Зверніть увагу на неглибоке копіювання під час використання оператора розвороту. Дивіться, наприклад: stackoverflow.com/questions/43638938/…
Жоао Маркос Грис

7
Якщо ви використовуєте той самий стан із його диспетчером, вам слід скористатися функцією. setExampleState( exampleState => ({...exampleState, masterField2: {...etc} }) );
Майкл Харлі

42

Якщо хтось шукає useState () гачки оновлення для об’єкта

- Through Input

        const [state, setState] = useState({ fName: "", lName: "" });
        const handleChange = e => {
            const { name, value } = e.target;
            setState(prevState => ({
                ...prevState,
                [name]: value
            }));
        };

        <input
            value={state.fName}
            type="text"
            onChange={handleChange}
            name="fName"
        />
        <input
            value={state.lName}
            type="text"
            onChange={handleChange}
            name="lName"
        />
   ***************************

 - Through onSubmit or button click
    
        setState(prevState => ({
            ...prevState,
            fName: 'your updated value here'
         }));

3
Саме те, що я шукав. Чудово!
StackedActor

41

Як правило, слід стежити за глибоко вкладеними об'єктами в стані React. Щоб уникнути несподіваної поведінки, стан слід оновити незмінно. Коли у вас є глибокі предмети, ви в кінцевому підсумку глибоко їх клонуєте для незмінності, що в React може бути досить дорогим. Чому?

Як тільки ви глибоко клонуєте стан, React перерахує і повторно відтворить все, що залежить від змінних, хоча вони не змінилися!

Тож, перш ніж намагатися вирішити своє питання, подумайте, як ви можете спочатку розгладити державу. Як тільки ви це зробите, ви знайдете прекрасні інструменти, які допоможуть мати справу з великими державами, такі як useReducer ().

Якщо ви думали про це, але все ще впевнені, що вам потрібно використовувати глибоко вкладене дерево стану, ви все одно можете використовувати useState () з бібліотеками, такими як immutable.js та Immutability-helper . Вони спрощують оновлення або клонування глибоких об’єктів, не турбуючись про змінність.


Ви також можете використовувати Hookstate (застереження: я автор) для управління складними (локальними та глобальними) даними стану без глибокого клонування тоді і не турбуючись про непотрібні оновлення - Hookstate зробить це за вас.
Ендрю

7

Дякую, Філіп, це мені допомогло - у моєму випадку було те, що я мав форму з великою кількістю полів введення, тому я підтримував початковий стан як об'єкт, і я не зміг оновити стан об'єкта. Мені допомог наведений вище запис :)

const [projectGroupDetails, setProjectGroupDetails] = useState({
    "projectGroupId": "",
    "projectGroup": "DDD",
    "project-id": "",
    "appd-ui": "",
    "appd-node": ""    
});

const inputGroupChangeHandler = (event) => {
    setProjectGroupDetails((prevState) => ({
       ...prevState,
       [event.target.id]: event.target.value
    }));
}

<Input 
    id="projectGroupId" 
    labelText="Project Group Id" 
    value={projectGroupDetails.projectGroupId} 
    onChange={inputGroupChangeHandler} 
/>



6

Я запізнився на вечірку .. :)

Відповідь @aseferov працює дуже добре, коли намір полягає у повторному введенні всієї структури об’єкта. Однак, якщо ціллю / метою є оновлення певного значення поля в Об’єкті, я вважаю, що підхід нижче є кращим.

ситуація:

const [infoData, setInfoData] = useState({
    major: {
      name: "John Doe",
      age: "24",
      sex: "M",
    },

    minor:{
      id: 4,
      collegeRegion: "south",

    }

  });

Оновлення конкретного запису вимагатиме відкликання попереднього штату prevState

Тут:

setInfoData((prevState) => ({
      ...prevState,
      major: {
        ...prevState.major,
        name: "Tan Long",
      }
    }));

можливо

setInfoData((prevState) => ({
      ...prevState,
      major: {
        ...prevState.major,
        name: "Tan Long",
      },
      minor: {
        ...prevState.minor,
        collegeRegion: "northEast"

    }));

Сподіваюся, це допомагає тим, хто намагається вирішити подібну проблему.


У мене є питання. Чому нам потрібно обертати функцію в дужках? Я не впевнений, що відбувається під капотом. Ви могли б знати або де я можу прочитати більше про це?
Ксенон

1
@MichaelRamage Чому ми обертаємо функцію в дужки:: ()Відповісти просто; це тому, що setInfoData має високий порядок за своєю природою, тобто він може взяти на себе іншу функцію як аргумент, люб'язно надаючи потужність, надану Хуком: useState. Сподіваюся, ця стаття надасть більше ясності щодо функцій вищого порядку: sitepoint.com/higher-order-functions-javascript
Оламігоке Філіп

1
Дуже корисно, особливо для випадку, коли ви намагаєтесь оновити властивість, вкладену в кілька шарів глибоко, тобто: first.second.third - інші приклади охоплюють first.second, але не first.second.third
Craig Presti - MSFT

3
function App() {

  const [todos, setTodos] = useState([
    { id: 1, title: "Selectus aut autem", completed: false },
    { id: 2, title: "Luis ut nam facilis et officia qui", completed: false },
    { id: 3, title: "Fugiat veniam minus", completed: false },
    { id: 4, title: "Aet porro tempora", completed: true },
    { id: 5, title: "Laboriosam mollitia et enim quasi", completed: false }
  ]);

  const changeInput = (e) => {todos.map(items => items.id === parseInt(e.target.value) && (items.completed = e.target.checked));
 setTodos([...todos], todos);}
  return (
    <div className="container">
      {todos.map(items => {
        return (
          <div key={items.id}>
            <label>
<input type="checkbox" 
onChange={changeInput} 
value={items.id} 
checked={items.completed} />&nbsp; {items.title}</label>
          </div>
        )
      })}
    </div>
  );
}

1

Я думаю, що найкращим рішенням є Immer . Це дозволяє вам оновлювати об'єкт, як ви безпосередньо змінюєте поля (masterField.fieldOne.fieldx = 'abc'). Але це, звичайно, не змінить фактичний об'єкт. Він збирає всі оновлення для чорнового об'єкта і надає вам кінцевий об'єкт в кінці, який ви можете використовувати для заміни вихідного об'єкта.


0

Я залишаю вам функцію утиліти для незмінного оновлення об'єктів

/**
 * Inmutable update object
 * @param  {Object} oldObject     Object to update
 * @param  {Object} updatedValues Object with new values
 * @return {Object}               New Object with updated values
 */
export const updateObject = (oldObject, updatedValues) => {
  return {
    ...oldObject,
    ...updatedValues
  };
};

Тож ви можете використовувати його таким чином

const MyComponent = props => {

  const [orderForm, setOrderForm] = useState({
    specialities: {
      elementType: "select",
      elementConfig: {
        options: [],
        label: "Specialities"
      },
      touched: false
    }
  });


// I want to update the options list, to fill a select element

  // ---------- Update with fetched elements ---------- //

  const updateSpecialitiesData = data => {
    // Inmutably update elementConfig object. i.e label field is not modified
    const updatedOptions = updateObject(
      orderForm[formElementKey]["elementConfig"],
      {
        options: data
      }
    );
    // Inmutably update the relevant element.
    const updatedFormElement = updateObject(orderForm[formElementKey], {
      touched: true,
      elementConfig: updatedOptions
    });
    // Inmutably update the relevant element in the state.
    const orderFormUpdated = updateObject(orderForm, {
      [formElementKey]: updatedFormElement
    });
    setOrderForm(orderFormUpdated);
  };

  useEffect(() => {
      // some code to fetch data
      updateSpecialitiesData.current("specialities",fetchedData);
  }, [updateSpecialitiesData]);

// More component code
}

Якщо ні, у вас є більше утиліт тут: https://es.reactjs.org/docs/update.html


0

Спочатку я використовував об'єкт в useState, але потім перейшов до useReducer-гачка для складних випадків. Я відчув покращення продуктивності, коли переробив код.

useReducer зазвичай переважніший useState, коли у вас складна логіка стану, яка включає кілька підзначень, або коли наступний стан залежить від попереднього.

useReducer React docs

Я вже реалізував такий гачок для власного використання:

/**
 * Same as useObjectState but uses useReducer instead of useState
 *  (better performance for complex cases)
 * @param {*} PropsWithDefaultValues object with all needed props 
 * and their initial value
 * @returns [state, setProp] state - the state object, setProp - dispatch 
 * changes one (given prop name & prop value) or multiple props (given an 
 * object { prop: value, ...}) in object state
 */
export function useObjectReducer(PropsWithDefaultValues) {
  const [state, dispatch] = useReducer(reducer, PropsWithDefaultValues);

  //newFieldsVal={[field_name]: [field_value], ...}
  function reducer(state, newFieldsVal) {
    return { ...state, ...newFieldsVal };
  }

  return [
    state,
    (newFieldsVal, newVal) => {
      if (typeof newVal !== "undefined") {
        const tmp = {};
        tmp[newFieldsVal] = newVal;
        dispatch(tmp);
      } else {
        dispatch(newFieldsVal);
      }
    },
  ];
}

більше пов'язаних гачків .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.