ব্যবহারের স্টেট সেট পদ্ধতি অবিলম্বে পরিবর্তন প্রতিফলিত করে না


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হুক সরবরাহিত আপডেটেটর ব্যবহার করে রাষ্ট্রীয় আপডেটগুলিও অবিচ্ছিন্ন, এবং অবিলম্বে প্রতিফলিত হবে না

এছাড়াও এখানে মূল ইস্যুটি কেবল অ্যাসিক্রোনাস প্রকৃতিই নয় বরং রাষ্ট্রীয় মূল্যবোধগুলি তাদের বর্তমান বন্ধ এবং রাজ্যের আপডেটের উপর ভিত্তি করে ফাংশন দ্বারা ব্যবহৃত হয় তা পরবর্তী পুনর্নির্মাণে প্রতিফলিত হবে যার দ্বারা বিদ্যমান বন্ধগুলি প্রভাবিত হয় না তবে নতুন তৈরি হয় । এখন বর্তমান অবস্থায় হুকের মধ্যে মানগুলি বিদ্যমান ক্লোজারগুলি দ্বারা প্রাপ্ত হয় এবং যখন পুনরায় রেন্ডার হয় তখন ফাংশনটি পুনরায় তৈরি করা হয় কিনা তার ভিত্তিতে ক্লোজারগুলি আপডেট করা হয় based

এমনকি যদি আপনি 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
হাই, কোনও ফর্মের মধ্যে হ্যান্ডলার জমা দেওয়ার মধ্যে ইউজস্টেট কল করার কী আছে? আমি একটি জটিল ফর্মটি যাচাইকরণের জন্য কাজ করছি, এবং আমি সাবমিট হ্যান্ডলারের ইউজেস্ট হুকের ভিতরে কল করছি এবং দুর্ভাগ্যক্রমে পরিবর্তনগুলি তাত্ক্ষণিকভাবে নয়!
দা 45

2
useEffectযদিও এটি সেরা সমাধান হতে পারে না, যেহেতু এটি অ্যাসিক্রোনাস কলগুলি সমর্থন করে না। সুতরাং, আমরা যদি moviesরাষ্ট্রীয় পরিবর্তন সম্পর্কে কিছু অ্যাসিনক্রোনাস বৈধতা তৈরি করতে চাই, তবে আমাদের এটির কোনও নিয়ন্ত্রণ নেই।
আরএ

2
দয়া করে মনে রাখবেন যে পরামর্শটি খুব ভাল, কারণটির ব্যাখ্যাটি আরও উন্নত করা যেতে পারে - the updater provided by useState hook অ্যাসিঙ্ক্রোনাস কিনা তা এই সত্যের সাথে কিছুই করার নয় , এর বিপরীতে সিঙ্ক্রোনাস this.stateথাকলে পরিবর্তিত হতে পারত this.setState, চারপাশের বন্ধটি const moviesএকই রকম থাকবে এমনকি যদি useStateএকটি সিঙ্ক্রোনাস ফাংশন সরবরাহ করেছে - আমার উত্তরের উদাহরণ দেখুন
এপ্রিলিয়ন

setMovies(prevMovies => ([...prevMovies, ...result]));আমার জন্য কাজ করেছেন
মিহির

এটি ভুল ফলাফলটি লগ করছে কারণ আপনি একটি বাসি বন্ধের লগইন করছেন না কারণ সেটারটি অ্যাসিনক্রোনাস। যদি অ্যাসিঙ্ক সমস্যাটি ছিল তবে আপনি সময়সীমা পেরোনোর ​​পরে লগ করতে পারেন, তবে আপনি এক ঘন্টা সময়সীমা নির্ধারণ করতে পারেন এবং এখনও ভুল ফলাফলটি লগ করতে পারেন কারণ অ্যাসিঙ্কটি সমস্যাটির কারণ নয়।
এইচএমআর

126

পূর্বের উত্তরে অ্যাডিয়োনাল বিবরণ :

প্রতিক্রিয়া যখন setState হয় অ্যাসিঙ্ক্রোনাস (উভয় ক্লাস এবং আঙ্গুলসমূহ), এবং এটি আসলে ব্যবহার করতে পর্যবেক্ষিত আচরণ ব্যাখ্যা করার ব্যাপারে প্রলুব্ধ, তাই নয় কি কারণে এটা ঘটে।

টিএলডিআর: কারণটি একটি অপরিবর্তনীয় মানের কাছাকাছি একটি ক্লোজার স্কোপ 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)
    }, [])
    

কেন এটি ঘটে তা ব্যাখ্যা:

অ্যাসিঙ্ক যদি একমাত্র কারণ ছিল, এটি সম্ভব হত await setState()

হোয়ারভার, উভয়ই propsএবং 1 টি রেন্ডারের সময় অপরিবর্তনীয়state বলে ধরে নেওয়া হয়

এমন আচরণ করুন 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
@ কেন্টকডবস নিবন্ধ (নীচে রেফ) অনুসরণ করে আমি সম্প্রতি ব্যবহারের সাথে একটি পুনর্লিখন শেষ করেছি, যা সত্যিই আমাকে একটি দৃ result় ফলাফল দিয়েছে যা এই ক্লোজার সমস্যাগুলি থেকে একটিও নয়। (রেফ: কেন্টকড্ডসডব্লব / ব্লগ / হাও- টু- ইউজ- রিরেট- কনটেক্সট- কার্যকরভাবে )
আল

1

আমি সম্প্রতি কেন্টকডবস নিবন্ধ (নীচে রেফারেট) অনুসরণ করে ব্যবহারের সাথে একটি পুনর্লিখনটি শেষ করেছি, যা সত্যিই আমাকে একটি দৃ result় ফলাফল দিয়েছে যা এই ক্লোজার সমস্যাগুলির থেকে একটুকুও ক্ষতিগ্রস্থ হয় না।

দেখুন: https://kentcdodds.com/blog/how-to-use-react-context-effectively

আমি তাঁর পঠনযোগ্য বয়লারপ্লেটটি আমার প্রিয় স্তরের ডিআরওয়াইনে সংশ্লেষ করেছি - তার স্যান্ডবক্স বাস্তবায়ন আপনাকে দেখায় যে এটি আসলে কীভাবে কাজ করে।

উপভোগ করুন, আমি জানি আমি!

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আপডেট হওয়া চলচ্চিত্রের অবজেক্টটি দেখতে পাবেন।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.