हुक सेट्टर का उपयोग गलत तरीके से राज्य को अधिलेखित कर देता है


31

यहाँ समस्या है: मैं एक बटन क्लिक पर 2 फ़ंक्शन कॉल करने का प्रयास कर रहा हूं। दोनों फ़ंक्शन राज्य को अपडेट करते हैं (मैं उपयोग कर रहा हूं हुक)। पहला फ़ंक्शन वैल्यू 1 को 'नया 1' के लिए सही ढंग से अपडेट करता है, लेकिन 1 एस (सेटटाइमआउट) के बाद दूसरा फ़ंक्शन फायर करता है, और यह मान 2 से 'नया 2' में बदल जाता है! इसने मान 1 को '1' पर वापस सेट किया। ये क्यों हो रहा है? अग्रिम में धन्यवाद!

import React, { useState } from "react";

const Test = () => {
  const [state, setState] = useState({
    value1: "1",
    value2: "2"
  });

  const changeValue1 = () => {
    setState({ ...state, value1: "new 1" });
  };
  const changeValue2 = () => {
    setState({ ...state, value2: "new 2" });
  };

  return (
    <>
      <button
        onClick={() => {
          changeValue1();
          setTimeout(changeValue2, 1000);
        }}
      >
        CHANGE BOTH
      </button>
      <h1>{state.value1}</h1>
      <h1>{state.value2}</h1>
    </>
  );
};

export default Test;

क्या आप राज्य की शुरुआत में लॉग इन कर सकते हैं changeValue2?
DanStarns

1
मैं अत्यधिक अनुशंसा करता हूं कि आप ऑब्जेक्ट को दो अलग-अलग कॉल में विभाजित करें useStateया इसके बजाय उपयोग करें useReducer
जेरेड स्मिथ

हाँ - यह दूसरा। उपयोग करने के लिए बस दो कॉल का उपयोग करें ()
स्कोव पेडर्सन

const [state, ...], और फिर सेटर में इसका जिक्र करते हुए ... यह हर समय एक ही राज्य का उपयोग करेगा।
जोहान्स कुह्न

कार्रवाई का सबसे अच्छा तरीका: 2 अलग-अलग useStateकॉल का उपयोग करें , प्रत्येक "चर" के लिए।
दिमा तिस्नेक

जवाबों:


30

बंद नरक में आपका स्वागत है । यह समस्या तब होती है क्योंकि जब भी setStateबुलाया जाता है, stateतो एक नया मेमोरी संदर्भ प्राप्त होता है, लेकिन फ़ंक्शन changeValue1और changeValue2, बंद होने के कारण, पुराने प्रारंभिक stateसंदर्भ को रखें।

एक समाधान सुनिश्चित करने के लिए setStateसे changeValue1और changeValue2नवीनतम राज्य एक का उपयोग करना है हो जाता है कॉलबैक (एक पैरामीटर के रूप पहले वाली स्थिति वाले):

import React, { useState } from "react";

const Test = () => {
  const [state, setState] = useState({
    value1: "1",
    value2: "2"
  });

  const changeValue1 = () => {
    setState((prevState) => ({ ...prevState, value1: "new 1" }));
  };
  const changeValue2 = () => {
    setState((prevState) => ({ ...prevState, value2: "new 2" }));
  };

  // ...
};

आप यहाँ और यहाँ इस बंद मुद्दे के बारे में व्यापक चर्चा पा सकते हैं ।


UseState हुक के साथ एक कॉलबैक एक हो रहा है गैर-दस्तावेजी सुविधा आपको लगता है कि काम करता है कर रहे हैं?
HMR

@ HMR हाँ, यह काम करता है और यह एक अन्य पेज पर प्रलेखित है। "कार्यात्मक अपडेट्स" अनुभाग यहाँ पर एक नज़र डालें: reactjs.org/docs/hooks-reference.html ( "नए राज्य पहले वाली स्थिति का उपयोग कर गणना की जाती है, तो आप एक समारोह setState को पारित कर सकते हैं")
अल्बर्टो Trindade Tavares

1
@AlbertoTrindadeTavares हां, मैं डॉक्स को अस्वस्थ देख रहा था, कुछ भी नहीं पा रहा था। उत्तर के लिए बहुत बहुत धन्यवाद!
बार्टेक

1
आपका पहला समाधान सिर्फ एक "आसान समाधान" नहीं है, यह सही तरीका है। दूसरा केवल तभी काम करेगा जब घटक एक सिंगलटन के रूप में डिज़ाइन किया गया है, और फिर भी मुझे इस बारे में निश्चित नहीं है क्योंकि राज्य हर बार एक नई वस्तु बन जाता है।
शिमोनस्टर

1
धन्यवाद @AlbertoTrindadeTavares! एक अच्छा
जोस सालगाडो

19

आपके कार्य इस तरह होने चाहिए:

const changeValue1 = () => {
    setState((prevState) => ({ ...prevState, value1: "new 1" }));
};
const changeValue2 = () => {
    setState((prevState) => ({ ...prevState, value2: "new 2" }));
};

इस प्रकार आप यह सुनिश्चित करते हैं कि जब आप कार्रवाई निकालते हैं तो आप पिछली स्थिति का उपयोग करके वर्तमान स्थिति में किसी भी मौजूदा संपत्ति को याद नहीं कर रहे हैं। इस प्रकार आप को बंद करने का प्रबंधन करने से बचना होगा।


6

जब changeValue2आह्वान किया जाता है, तो प्रारंभिक अवस्था का आयोजन किया जाता है, इसलिए राज्य वापस वैवाहिक स्थिति में लौट आता है और फिर value2संपत्ति लिखी जाती है।

अगली बार जब changeValue2यह लागू किया जाता है, तो यह राज्य धारण करता है {value1: "1", value2: "new 2"}, इसलिए value1संपत्ति अधिलेखित हो जाती है।

आपको setStateपैरामीटर के लिए एक तीर फ़ंक्शन की आवश्यकता है ।

const Test = () => {
  const [state, setState] = React.useState({
    value1: "1",
    value2: "2"
  });

  const changeValue1 = () => {
    setState(prev => ({ ...prev, value1: "new 1" }));
  };
  const changeValue2 = () => {
    setState(prev => ({ ...prev, value2: "new 2" }));
  };

  return (
    <React.Fragment>
      <button
        onClick={() => {
          changeValue1();
          setTimeout(changeValue2, 1000);
        }}
      >
        CHANGE BOTH
      </button>
      <h1>{state.value1}</h1>
      <h1>{state.value2}</h1>
    </React.Fragment>
  );
};

ReactDOM.render(<Test />, 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>


3

क्या हो रहा है कि दोनों changeValue1और changeValue2राज्य को उस रेंडर से देखते हैं जो वे बनाए गए थे , इसलिए जब आपका घटक पहली बार इन 2 कार्यों को देख सकता है:

state= {
  value1: "1",
  value2: "2"
}

जब आप बटन पर क्लिक करते हैं, changeValue1तो पहले कहा जाता है और राज्य को {value1: "new1", value2: "2"}अपेक्षित रूप से बदलता है।

अब, 1 सेकंड के बाद, changeValue2कहा जाता है, लेकिन यह फ़ंक्शन अभी भी प्रारंभिक स्थिति ( {value1; "1", value2: "2"}) देखता है , इसलिए जब यह फ़ंक्शन इस तरह से राज्य को अपडेट करता है:

setState({ ...state, value2: "new 2" });

आप देखते हुए समाप्त करते हैं {value1; "1", value2: "new2"}:।

स्रोत

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.