React Hooks useState () ile Object


103

React with Hooks'ta durumu güncellemenin doğru yolu nedir, yuvalanmış bir nesne mi?

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

İnsan nasıl kullanacağı setExampleStategüncelleme exampleStateiçin a(bir alan ekleme)?

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

b (Değerleri değiştirme)?

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


Mevcut nesneye yeni nesne anahtarı değeri eklemeyi mi kastediyorsunuz?
kodlayın

@Justcode İlk örnek için evet, ikinci örnek için sadece mevcut nesneyi değiştiriyor
isaacsultan

Yanıtlar:


109

Böyle yeni bir değer geçirebilirsin

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

2
Harika, bu bölüm a'yı yanıtlıyor! b bölümü için en iyi uygulamayı biliyor musunuz?
isaacsultan

1
{// new values} masterFieldyerine aynı zamanda değişiyormasterField2 setExampleState({...exampleState, masterField:
aseferov

3
Yayma operatörünü kullanırken sığ kopyalamanın farkında olun. Örneğin bkz .: stackoverflow.com/questions/43638938/…
João Marcos Gris

7
Dispatcher ile aynı durumu kullanıyorsanız, bir işlev kullanmalısınız. setExampleState( exampleState => ({...exampleState, masterField2: {...etc} }) );
Michael Harley

43

Herhangi biri useState () arıyorsa , nesne için güncellemeyi kancalar

- 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
Tam olarak aradığım şey. Harika!
StackedActor

41

Genellikle React durumunda derinlemesine iç içe geçmiş nesnelere dikkat etmelisiniz. Beklenmedik davranışlardan kaçınmak için, durum değişmez bir şekilde güncellenmelidir. Derin nesneleriniz olduğunda, onları değişmezlik için derinlemesine klonlarsınız ve bu React'te oldukça pahalı olabilir. Neden?

Durumu derinlemesine klonladığınızda, React, değişmemiş olsalar bile değişkenlere bağlı her şeyi yeniden hesaplayacak ve yeniden oluşturacaktır!

Bu yüzden, sorununuzu çözmeye çalışmadan önce, durumu nasıl düzleştirebileceğinizi düşünün. Bunu yaptığınız anda, useReducer () gibi büyük durumlarla başa çıkmanıza yardımcı olacak güzel araçlar bulacaksınız.

Düşündüyseniz, ancak yine de derinlemesine iç içe geçmiş bir durum ağacı kullanmanız gerektiğine ikna olmuşsanız, useState () 'i immutable.js ve Immutability-helper gibi kitaplıklarla kullanmaya devam edebilirsiniz . Değişkenlik konusunda endişelenmenize gerek kalmadan derin nesneleri güncellemeyi veya klonlamayı kolaylaştırırlar.


Ayrıca Hookstate'i (sorumluluk reddi: Ben bir yazarım) karmaşık (yerel ve global) durum verilerini derin klonlama olmadan ve gereksiz güncellemeler hakkında endişelenmeden yönetmek için de kullanabilirsiniz - Hookstate sizin için halledecektir.
Andrew

7

Teşekkürler Philip bu bana yardımcı oldu - benim kullanım durumum çok fazla giriş alanı olan bir formumdu, bu yüzden ilk durumu nesne olarak tuttum ve nesne durumunu güncelleyemedim. Yukarıdaki yazı bana yardımcı oldu :)

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

Partiye geç kaldım .. :)

@aseferov yanıtı, amaç tüm nesne yapısına yeniden girmek olduğunda çok iyi çalışır. Bununla birlikte, hedef / hedef bir Nesnedeki belirli bir alan değerini güncellemekse, aşağıdaki yaklaşımın daha iyi olduğuna inanıyorum.

durum:

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

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

    }

  });

Belirli bir kaydı güncellemek, önceki Durumun geri çağrılmasını gerektirecektir. prevState

Buraya:

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

belki

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

    }));

Umarım bu, benzer bir sorunu çözmeye çalışan herkese yardımcı olur.


Bir sorum var. Neden işlevi parantez içine almamız gerekiyor? Kaputun altında neler olduğundan emin değilim. Bunun hakkında daha fazla bilgi edinebileceğim veya nerede okuyabileceğimi biliyor musunuz?
Xenon

1
@MichaelRamage Fonksiyonu neden parantez içine alıyoruz ():: Bunu basitçe yanıtlamak için; çünkü bu kadar setInfoData Bir argüman olarak başka fonksiyonu üzerine alabilir yani doğası gereği yüksek mertebede olan, Hook tarafından sağlanan gücün nezaket: USESTATE. Bu makale daha yüksek dereceden fonksiyonlar üzerinde daha fazla netlik sağlayacak umut: sitepoint.com/higher-order-functions-javascript
Olamigoke Philip

1
Özellikle birkaç katman derinliğinde yuvalanmış bir özelliği güncellemeye çalıştığınız durumlarda çok yararlıdır, yani: birinci.ikinci.Üçüncü - diğer örnekler birinci. saniyeyi kapsar, ancak birinci. saniye.üçüncü değil
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

En iyi çözümün Immer olduğunu düşünüyorum . Alanları doğrudan değiştirir gibi nesneyi güncellemenize izin verir (masterField.fieldOne.fieldx = 'abc'). Ama elbette gerçek nesneyi değiştirmeyecek. Bir taslak nesneyle ilgili tüm güncellemeleri toplar ve sonunda size orijinal nesneyi değiştirmek için kullanabileceğiniz son bir nesne verir.


0

Nesneleri değişmez bir şekilde güncellemek için size bir yardımcı program işlevi bırakıyorum

/**
 * 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
  };
};

Yani bunu böyle kullanabilirsin

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
}

Değilse, burada daha fazla yardımcı programınız yoksa: https://es.reactjs.org/docs/update.html


0

Başlangıçta useState'te object kullandım, ancak daha sonra karmaşık durumlar için useReducer hook'a geçtim. Kodu yeniden düzenlediğimde bir performans artışı hissettim.

Birden çok alt değer içeren karmaşık durum mantığınız olduğunda veya sonraki durum öncekine bağlı olduğunda useReducer genellikle useState yerine tercih edilir.

useReducer React belgeleri

Böyle bir kancayı kendi kullanımım için zaten uyguladım:

/**
 * 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);
      }
    },
  ];
}

daha ilgili kancalar .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.