React Hooks useState () với Object


103

Cách chính xác để cập nhật trạng thái, một đối tượng lồng nhau, trong React with Hooks là gì?

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

Làm thế nào người ta sẽ sử dụng setExampleStateđể cập nhật exampleStatetới a(phụ thêm một trường)?

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

b (Thay đổi giá trị)?

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


ý bạn là thêm giá trị khóa đối tượng mới vào đối tượng hiện có?
Chỉ cần mã

@Justcode Đối với ví dụ đầu tiên có, cho ví dụ thứ hai chỉ thay đổi đối tượng hiện có
isaacsultan

Câu trả lời:


109

Bạn có thể chuyển giá trị mới như thế này

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

2
Tuyệt vời, câu trả lời phần a! bạn có biết cách thực hành tốt nhất cho phần b không?
isaacsultan

1
thay đổi cũng tương tự chỉ là quá khứ masterFieldthay vì masterField2 setExampleState({...exampleState, masterField:{// giá trị mới}
aseferov

3
Lưu ý sao chép nông khi sử dụng toán tử spread. Xem ví dụ: stackoverflow.com/questions/43638938/...
João Marcos Gris

7
Nếu bạn đang sử dụng cùng một trạng thái với Dispatcher của nó, bạn nên sử dụng một hàm. setExampleState( exampleState => ({...exampleState, masterField2: {...etc} }) );
Michael Harley

43

Nếu có ai đang tìm kiếm bản cập nhật hooks useState () cho đối tượng

- 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
Chính xác những gì tôi đang tìm kiếm. Tuyệt vời!
StackedActor

41

Nói chung, bạn nên để ý các đối tượng lồng nhau sâu trong trạng thái React. Để tránh hành vi không mong muốn, trạng thái nên được cập nhật liên tục. Khi bạn có các đối tượng sâu, bạn sẽ nhân bản sâu chúng để không thay đổi, điều này có thể khá tốn kém trong React. Tại sao?

Khi bạn nhân bản sâu trạng thái, React sẽ tính toán lại và hiển thị lại mọi thứ phụ thuộc vào các biến, mặc dù chúng không thay đổi!

Vì vậy, trước khi cố gắng giải quyết vấn đề của bạn, hãy nghĩ cách bạn có thể làm phẳng trạng thái trước. Ngay sau khi bạn làm điều đó, bạn sẽ tìm thấy các công cụ tuyệt đẹp sẽ giúp xử lý các trạng thái lớn, chẳng hạn như useReducer ().

Trong trường hợp bạn đã nghĩ về nó, nhưng vẫn bị thuyết phục rằng bạn cần sử dụng cây trạng thái lồng nhau sâu, bạn vẫn có thể sử dụng useState () với các thư viện như immutable.jsImmutability-helper . Chúng giúp bạn dễ dàng cập nhật hoặc sao chép các đối tượng sâu mà không phải lo lắng về khả năng thay đổi.


Bạn cũng có thể sử dụng Hookstate (từ chối trách nhiệm: Tôi là tác giả) để quản lý dữ liệu trạng thái phức tạp (cục bộ và toàn cầu) mà không cần sao chép sâu sau đó và không cần lo lắng về các cập nhật không cần thiết - Hookstate sẽ xử lý nó cho bạn.
Andrew

7

Cảm ơn Philip, điều này đã giúp tôi - trường hợp sử dụng của tôi là tôi có một biểu mẫu với rất nhiều trường đầu vào vì vậy tôi đã duy trì trạng thái ban đầu là đối tượng và tôi không thể cập nhật trạng thái đối tượng. Bài đăng trên đã giúp tôi :)

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

Tôi đến muộn bữa tiệc .. :)

Câu trả lời @aseferov hoạt động rất tốt khi có ý định nhập lại toàn bộ cấu trúc đối tượng. Tuy nhiên, nếu mục tiêu / mục tiêu là cập nhật giá trị trường cụ thể trong Đối tượng, tôi tin rằng cách tiếp cận bên dưới là tốt hơn.

tình hình:

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

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

    }

  });

Việc cập nhật một bản ghi cụ thể sẽ yêu cầu thu hồi về Trạng thái trước đó prevState

Đây:

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

có lẽ

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

    }));

Tôi hy vọng điều này sẽ giúp bất kỳ ai đang cố gắng giải quyết một vấn đề tương tự.


Tôi có một câu hỏi. Tại sao chúng ta cần bao hàm trong ngoặc đơn? Tôi không chắc những gì đang xảy ra dưới mui xe. Bạn có tình cờ biết hoặc nơi tôi có thể đọc thêm về điều đó?
Xenon

1
@MichaelRamage Tại sao chúng ta đặt hàm trong ngoặc đơn ():: Để trả lời điều này một cách đơn giản; bởi vì setInfoData nó có thứ tự cao về bản chất, tức là Nó có thể đảm nhận một chức năng khác làm đối số, nhờ sức mạnh được cung cấp bởi Hook: useState. Tôi hy vọng bài viết này sẽ cung cấp rõ ràng thêm về chức năng bậc cao: sitepoint.com/higher-order-functions-javascript
Olamigoke Philip

1
Rất hữu ích, đặc biệt đối với trường hợp bạn đang cố gắng cập nhật một thuộc tính được lồng nhiều lớp sâu, tức là: first.second.third - các ví dụ khác bao gồm first.second, nhưng không bao gồm 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

Tôi nghĩ giải pháp tốt nhất là Immer . Nó cho phép bạn cập nhật đối tượng giống như bạn đang sửa đổi trực tiếp các trường (masterField.fieldOne.fieldx = 'abc'). Nhưng nó sẽ không thay đổi đối tượng thực tế tất nhiên. Nó thu thập tất cả các cập nhật trên một đối tượng nháp và cung cấp cho bạn một đối tượng cuối cùng ở cuối mà bạn có thể sử dụng để thay thế đối tượng gốc.


0

Tôi để lại cho bạn một chức năng tiện ích để cập nhật các đối tượng một cách tự nhiên

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

Vì vậy, bạn có thể sử dụng nó như thế này

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
}

Nếu không, bạn có thêm tiện ích ở đây: https://es.reactjs.org/docs/update.html


0

Ban đầu tôi sử dụng đối tượng trong useState, nhưng sau đó tôi chuyển sang hook useReducer cho các trường hợp phức tạp. Tôi cảm thấy sự cải thiện về hiệu suất khi tôi cấu trúc lại mã.

useReducer thường được ưu tiên sử dụngState khi bạn có logic trạng thái phức tạp liên quan đến nhiều giá trị con hoặc khi trạng thái tiếp theo phụ thuộc vào trạng thái trước đó.

tài liệu useReducer React

Tôi đã triển khai hook như vậy để sử dụng cho riêng mình:

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

nhiều móc liên quan hơn .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.