उपयोग सेट विधि तुरंत परिवर्तन को प्रतिबिंबित नहीं करती है


168

मैं हुक सीखने की कोशिश कर रहा हूं और useStateविधि ने मुझे भ्रमित कर दिया है। मैं एक सरणी के रूप में एक राज्य के लिए एक प्रारंभिक मूल्य प्रदान कर रहा हूं। में सेट विधि useStateमेरे साथ spread(...)या उसके लिए भी काम नहीं कर रही है without spread operator। मैंने दूसरे पीसी पर एक एपीआई बनाया है जिसे मैं कॉल कर रहा हूं और उस डेटा को प्राप्त कर रहा हूं जिसे मैं राज्य में स्थापित करना चाहता हूं।

यहाँ मेरा कोड है:

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const StateSelector = () => {
  const initialValue = [
    {
      category: "",
      photo: "",
      description: "",
      id: 0,
      name: "",
      rating: 0
    }
  ];

  const [movies, setMovies] = useState(initialValue);

  useEffect(() => {
    (async function() {
      try {
        //const response = await fetch(
        //`http://192.168.1.164:5000/movies/display`
        //);
        //const json = await response.json();
        //const result = json.data.result;
        const result = [
          {
            category: "cat1",
            description: "desc1",
            id: "1546514491119",
            name: "randomname2",
            photo: null,
            rating: "3"
          },
          {
            category: "cat2",
            description: "desc1",
            id: "1546837819818",
            name: "randomname1",
            rating: "5"
          }
        ];
        console.log(result);
        setMovies(result);
        console.log(movies);
      } catch (e) {
        console.error(e);
      }
    })();
  }, []);

  return <p>hello</p>;
};

const rootElement = document.getElementById("root");
ReactDOM.render(<StateSelector />, rootElement);

setMovies(result)के साथ-साथ setMovies(...result)काम नहीं कर रहा। यहां कुछ मदद का उपयोग कर सकता है।

मुझे उम्मीद है कि परिणाम चर को फिल्मों की श्रेणी में धकेला जाएगा।

जवाबों:


220

क्लास के घटकों में बहुत कुछ पसंद है , जो उपलब्ध कराए गए अपडेटर React.Componentया React.PureComponentस्टेट अपडेट द्वारा बनाया गया हैuseState हुक भी अतुल्यकालिक है, और तुरंत प्रतिबिंबित नहीं करेगा

यहाँ भी मुख्य मुद्दा केवल अतुल्यकालिक प्रकृति का नहीं है, बल्कि इस तथ्य का भी है कि राज्य मूल्यों का उपयोग उनके वर्तमान क्लोजर के आधार पर किया जाता है और राज्य अपडेट अगले री-रेंडर में परिलक्षित होगा जिसके द्वारा मौजूदा क्लोज़र प्रभावित नहीं होते हैं लेकिन नए बनाए जाते हैं । अब वर्तमान स्थिति में हुक के भीतर के मान मौजूदा क्लोजर द्वारा प्राप्त किए जाते हैं और जब पुन: रेंडर होता है, तो क्लोजर को अपडेट किया जाता है कि फ़ंक्शन फिर से बनाया गया है या नहीं।

भले ही आप एक setTimeoutफ़ंक्शन जोड़ते हैं , हालांकि टाइमआउट कुछ समय बाद चलेगा, जिसके द्वारा पुन: रेंडर हुआ होगा, लेकिन सेटटिमाउट अभी भी अपने पिछले बंद से मूल्य का उपयोग करेगा और अपडेट नहीं किया जाएगा।

setMovies(result);
console.log(movies) // movies here will not be updated

यदि आप राज्य अद्यतन पर कोई कार्य करना चाहते हैं, तो आपको उपयोग करने की आवश्यकता होती है, हुक का उपयोग करना, जैसे componentDidUpdateकक्षा के घटकों का उपयोग करना , क्योंकि सेटर द्वारा उपयोग किए गए सेटर में कॉलबैक पैटर्न नहीं है।

useEffect(() => {
    // action on update of movies
}, [movies]);

जहाँ तक राज्य को अद्यतन करने के लिए वाक्य रचना का सवाल है, setMovies(result) में पिछले moviesमूल्य को एस्क्सेस अनुरोध से उपलब्ध होने के साथ बदल देगा

हालाँकि, यदि आप पहले से मौजूद मानों के साथ प्रतिक्रिया को मर्ज करना चाहते हैं, तो आपको स्टेट सिचुएशन के कॉलबैक सिंटैक्स का उपयोग करना चाहिए, साथ ही साथ सिंटैक्स के सही उपयोग के साथ

setMovies(prevMovies => ([...prevMovies, ...result]));

17
नमस्ते, एक फॉर्म सबमिट हैंडलर के अंदर कॉलिंग स्टेस्टेट के बारे में क्या? मैं एक जटिल रूप को मान्य करने पर काम कर रहा हूं, और मैं सबमिट करता हूं सबमिटहैंडलर उपयोग के साथ हुक और दुर्भाग्य से परिवर्तन तत्काल नहीं हैं!
दा ४५

2
useEffectहालांकि यह अतुल्यकालिक कॉल का समर्थन नहीं करता है, हालांकि सबसे अच्छा समाधान नहीं हो सकता है। इसलिए, यदि हम moviesराज्य परिवर्तन पर कुछ अतुल्यकालिक सत्यापन करना चाहते हैं, तो इस पर हमारा कोई नियंत्रण नहीं है।
आरए

2
कृपया ध्यान दें कि जबकि सलाह बहुत अच्छी है, कारण की व्याख्या में सुधार किया जा सकता है - इस तथ्य से कोई लेना-देना नहीं the updater provided by useState hook है कि क्या अतुल्यकालिक है, इसके विपरीत this.stateअगर this.setStateयह सिंक्रनाइज़ किया गया था, तो परस्पर बंद किया जा सकता था, const moviesभले ही चारों ओर एक ही बना रहेगा। useStateएक तुल्यकालिक फ़ंक्शन प्रदान किया - मेरे उत्तर में उदाहरण देखें
Apr26

setMovies(prevMovies => ([...prevMovies, ...result]));मेरे लिए काम किया
मिहिर

यह गलत परिणाम दर्ज कर रहा है क्योंकि आप एक बासी को बंद कर रहे हैं , क्योंकि सेटर अतुल्यकालिक नहीं है। यदि async समस्या थी तो आप एक टाइमआउट के बाद लॉग इन कर सकते थे, लेकिन आप एक घंटे के लिए टाइमआउट सेट कर सकते थे और अभी भी गलत परिणाम लॉग कर सकते हैं क्योंकि async समस्या नहीं है।
HMR

126

पिछले उत्तर के अतिरिक्त विवरण :

प्रतिक्रिया की जबकि setState है अतुल्यकालिक (दोनों वर्गों और हुक), और यह कि इस तथ्य का उपयोग करने के मनाया व्यवहार की व्याख्या अच्छा लगता है, यह नहीं है कारण है कि ऐसा होता है।

TLDR: कारण एक अपरिवर्तनीय मूल्य के आसपास एक बंद गुंजाइश है const


समाधान:

  • रेंडर फ़ंक्शन में मान पढ़ें (नेस्टेड फ़ंक्शन के अंदर नहीं):

    useEffect(() => { setMovies(result) }, [])
    console.log(movies)
  • चर को निर्भरता में जोड़ें (और प्रतिक्रिया-हुक / थकावट- डिप - एस्लिन नियम का उपयोग करें):

    useEffect(() => { setMovies(result) }, [])
    useEffect(() => { console.log(movies) }, [movies])
  • एक परस्पर संदर्भ का उपयोग करें (जब ऊपर संभव नहीं है):

    const moviesRef = useRef(initialValue)
    useEffect(() => {
      moviesRef.current = result
      console.log(moviesRef.current)
    }, [])

स्पष्टीकरण यह क्यों होता है:

यदि async एकमात्र कारण था, तो यह संभव होगा await setState()

Howerver, दोनों propsऔर stateकर रहे हैं के दौरान 1 प्रस्तुत करना अपरिवर्तनीय मान लिया

इलाज this.stateके रूप में अगर यह अपरिवर्तनीय थे।

हुक के साथ, कीवर्ड के साथ निरंतर मूल्यों का उपयोग करके इस धारणा को बढ़ाया जाता है const:

const [state, setState] = useState('initial')

मान 2 रेंडरर्स के बीच भिन्न हो सकता है, लेकिन रेंडर के अंदर और किसी भी क्लोजर के अंदर एक स्थिर रहता है (रेंडर समाप्त होने के बाद भी लंबे समय तक रहने वाले फ़ंक्शन, उदा।useEffect , इवेंट हैंडलर, किसी भी प्रॉमिस या सेटटाइमआउट के अंदर)।

नकली, लेकिन तुल्यकालिक , प्रतिक्रिया जैसी कार्यान्वयन पर विचार करें :

// sync implementation:

let internalState
let renderAgain

const setState = (updateFn) => {
  internalState = updateFn(internalState)
  renderAgain()
}

const useState = (defaultState) => {
  if (!internalState) {
    internalState = defaultState
  }
  return [internalState, setState]
}

const render = (component, node) => {
  const {html, handleClick} = component()
  node.innerHTML = html
  renderAgain = () => render(component, node)
  return handleClick
}

// test:

const MyComponent = () => {
  const [x, setX] = useState(1)
  console.log('in render:', x) // ✅
  
  const handleClick = () => {
    setX(current => current + 1)
    console.log('in handler/effect/Promise/setTimeout:', x) // ❌ NOT updated
  }
  
  return {
    html: `<button>${x}</button>`,
    handleClick
  }
}

const triggerClick = render(MyComponent, document.getElementById('root'))
triggerClick()
triggerClick()
triggerClick()
<div id="root"></div>


11
बहुत बढ़िया जवाब। आईएमओ, यह उत्तर आपको भविष्य में सबसे अधिक दिल टूटने से बचाएगा यदि आप इसे पढ़ने और अवशोषित करने के लिए पर्याप्त समय का निवेश करते हैं।
स्कॉट मैलॉन

यह एक रूटर के साथ संयोजन में एक संदर्भ का उपयोग करने के लिए बहुत कठिन बनाता है, हाँ? जैसा कि मैं अगले पृष्ठ पर जाता हूं कि राज्य चला गया है .. और मैं केवल उपयोग हुक के अंदर सेट नहीं कर सकता क्योंकि वे प्रस्तुत नहीं कर सकते ... मैं उत्तर की पूर्णता की सराहना करता हूं और यह बताता है कि मैं क्या देख रहा हूं, लेकिन मैं यह कैसे हरा करने के लिए पता नहीं कर सकते। मैं मानों को अद्यतन करने के लिए संदर्भ में कार्य कर रहा हूं जो तब प्रभावी रूप से जारी नहीं हैं ... इसलिए मुझे उन्हें संदर्भ में बंद होने से बाहर निकालना होगा, हां?
अल जोसलिन

@ एलजोसलिन पहली नज़र में, यह एक अलग समस्या की तरह लगता है, भले ही यह बंद होने की गुंजाइश के कारण हो। यदि आपके पास एक ठोस प्रश्न है, तो कृपया कोड उदाहरण और सभी के साथ एक नया
स्टैकओवरफ़्लो

1
वास्तव में मैंने अभी @Rentcdobs लेख (नीचे रेफरी) का उपयोग करते हुए, रीडरेडर के साथ एक पुनर्लेखन को समाप्त किया, जिसने मुझे वास्तव में एक ठोस परिणाम दिया जो इन बंद होने वाली समस्याओं से एक सा नहीं ग्रस्त है। (रेफ: kentcdodds.com/blog/how-to-use-react-context-effectively )
अल

1

मैंने @Rentcdobs लेख (नीचे रेफरी) का उपयोग करते हुए, बस उपयोगकर्ता के साथ एक फिर से लिखना समाप्त किया, जिसने मुझे वास्तव में एक ठोस परिणाम दिया जो इन बंद होने वाली समस्याओं से एक सा नहीं ग्रस्त है।

देखें: https://kentcdodds.com/blog/how-to-use-react-context-effectively

मैंने उसकी पठनीय बॉयलरप्लेट को मेरे पसंदीदा स्तर DRYness के लिए संघनित किया - उसके सैंडबॉक्स कार्यान्वयन को पढ़ने से आपको पता चलेगा कि यह वास्तव में कैसे काम करता है।

आनंद लें, मुझे पता है कि मैं हूँ !!

import React from 'react'

// ref: https://kentcdodds.com/blog/how-to-use-react-context-effectively

const ApplicationDispatch = React.createContext()
const ApplicationContext = React.createContext()

function stateReducer(state, action) {
  if (state.hasOwnProperty(action.type)) {
    return { ...state, [action.type]: state[action.type] = action.newValue };
  }
  throw new Error(`Unhandled action type: ${action.type}`);
}

const initialState = {
  keyCode: '',
  testCode: '',
  testMode: false,
  phoneNumber: '',
  resultCode: null,
  mobileInfo: '',
  configName: '',
  appConfig: {},
};

function DispatchProvider({ children }) {
  const [state, dispatch] = React.useReducer(stateReducer, initialState);
  return (
    <ApplicationDispatch.Provider value={dispatch}>
      <ApplicationContext.Provider value={state}>
        {children}
      </ApplicationContext.Provider>
    </ApplicationDispatch.Provider>
  )
}

function useDispatchable(stateName) {
  const context = React.useContext(ApplicationContext);
  const dispatch = React.useContext(ApplicationDispatch);
  return [context[stateName], newValue => dispatch({ type: stateName, newValue })];
}

function useKeyCode() { return useDispatchable('keyCode'); }
function useTestCode() { return useDispatchable('testCode'); }
function useTestMode() { return useDispatchable('testMode'); }
function usePhoneNumber() { return useDispatchable('phoneNumber'); }
function useResultCode() { return useDispatchable('resultCode'); }
function useMobileInfo() { return useDispatchable('mobileInfo'); }
function useConfigName() { return useDispatchable('configName'); }
function useAppConfig() { return useDispatchable('appConfig'); }

export {
  DispatchProvider,
  useKeyCode,
  useTestCode,
  useTestMode,
  usePhoneNumber,
  useResultCode,
  useMobileInfo,
  useConfigName,
  useAppConfig,
}

इस के समान उपयोग के साथ:

import { useHistory } from "react-router-dom";

// https://react-bootstrap.github.io/components/alerts
import { Container, Row } from 'react-bootstrap';

import { useAppConfig, useKeyCode, usePhoneNumber } from '../../ApplicationDispatchProvider';

import { ControlSet } from '../../components/control-set';
import { keypadClass } from '../../utils/style-utils';
import { MaskedEntry } from '../../components/masked-entry';
import { Messaging } from '../../components/messaging';
import { SimpleKeypad, HandleKeyPress, ALT_ID } from '../../components/simple-keypad';

export const AltIdPage = () => {
  const history = useHistory();
  const [keyCode, setKeyCode] = useKeyCode();
  const [phoneNumber, setPhoneNumber] = usePhoneNumber();
  const [appConfig, setAppConfig] = useAppConfig();

  const keyPressed = btn => {
    const maxLen = appConfig.phoneNumberEntry.entryLen;
    const newValue = HandleKeyPress(btn, phoneNumber).slice(0, maxLen);
    setPhoneNumber(newValue);
  }

  const doSubmit = () => {
    history.push('s');
  }

  const disableBtns = phoneNumber.length < appConfig.phoneNumberEntry.entryLen;

  return (
    <Container fluid className="text-center">
      <Row>
        <Messaging {...{ msgColors: appConfig.pageColors, msgLines: appConfig.entryMsgs.altIdMsgs }} />
      </Row>
      <Row>
        <MaskedEntry {...{ ...appConfig.phoneNumberEntry, entryColors: appConfig.pageColors, entryLine: phoneNumber }} />
      </Row>
      <Row>
        <SimpleKeypad {...{ keyboardName: ALT_ID, themeName: appConfig.keyTheme, keyPressed, styleClass: keypadClass }} />
      </Row>
      <Row>
        <ControlSet {...{ btnColors: appConfig.buttonColors, disabled: disableBtns, btns: [{ text: 'Submit', click: doSubmit }] }} />
      </Row>
    </Container>
  );
};

AltIdPage.propTypes = {};

अब सब कुछ मेरे सभी पृष्ठों में हर जगह आसानी से जारी है

अच्छा!

धन्यवाद केंट!


-2
// replace
return <p>hello</p>;
// with
return <p>{JSON.stringify(movies)}</p>;

अब आपको यह देखना चाहिए, कि आपका कोड वास्तव में काम करता है । क्या काम नहीं करता है console.log(movies)। इसकी वजह moviesहै पुराने राज्य की ओर इशारा करना । यदि आप वापसी के ठीक console.log(movies)बाहर अपने कदम रखते useEffectहैं, तो आप अपडेटेड मूवी ऑब्जेक्ट देखेंगे।

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