উপাদানগুলিতে ব্যবহারকারীর স্টেট থেকে একাধিক কল স্টেট আপডেটেটরে একাধিক পুনঃ-রেন্ডার কারণ


113

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

const getData = url => {

    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(async () => {

        const test = await api.get('/people')

        if(test.ok){
            setLoading(false);
            setData(test.data.results);
        }

    }, []);

    return { data, loading };
};

একটি সাধারণ শ্রেণীর উপাদানগুলিতে আপনি রাষ্ট্রকে আপডেট করার জন্য একক কল করতে পারেন যা একটি জটিল বিষয় হতে পারে তবে "হুক্স ওয়ে" বলে মনে হয় রাষ্ট্রকে ছোট ইউনিটে বিভক্ত করা হবে, যার একটি পার্শ্ব প্রতিক্রিয়া মনে হয় একাধিক পুনরায়- তারা পৃথকভাবে আপডেট করা হলে রেন্ডার দেয়। কোন ধারণা কীভাবে এটি প্রশমিত করবেন?


যদি আপনি নির্ভরশীল রাষ্ট্রগুলি ব্যবহার করেন তবে আপনার সম্ভবত ব্যবহার করা উচিতuseReducer
থিংলিনাক্স

কি দারুন! আমি কেবলমাত্র এটি সন্ধান করেছি এবং রেন্ডারিং কীভাবে প্রতিক্রিয়া দেখায় তা আমার ধারণাটিকে পুরোপুরি উড়িয়ে দিয়েছে। আমি এইভাবে কাজ করার জন্য কোনও সুবিধা বুঝতে পারি না - এটি বরং স্বেচ্ছাচারী বলে মনে হয় যে অ্যাসিঙ্ক কলব্যাকের আচরণটি সাধারণ ইভেন্ট হ্যান্ডলারের চেয়ে আলাদা। বিটিডাব্লু, আমার পরীক্ষাগুলিতে মনে হয় যে সমস্ত সেটস্টেট কলগুলি প্রক্রিয়া করা না হওয়া পর্যন্ত পুনর্মিলন (যেমন রিয়েল ডিওমের আপডেট) ঘটে না, সুতরাং মধ্যবর্তী রেন্ডার কলগুলি যেভাবেই নষ্ট হয় না।
অ্যান্ডি

উত্তর:


112

আপনি loadingরাষ্ট্র এবং dataরাষ্ট্রকে একটি রাষ্ট্রের বস্তুর সাথে সংযুক্ত করতে পারেন এবং তারপরে আপনি একটি setStateকল করতে পারেন এবং কেবলমাত্র একটি রেন্ডার থাকবে।

দ্রষ্টব্য:setState শ্রেণীর উপাদানগুলির বিপরীতে , setStateথেকে useStateপ্রাপ্তরা বিদ্যমান অবস্থার সাথে বস্তুগুলিকে একীভূত করে না, এটি বস্তুকে পুরোপুরি প্রতিস্থাপন করে। আপনি যদি একীভূত করতে চান তবে আপনাকে পূর্বের অবস্থাটি পড়তে হবে এবং নতুন মানগুলির সাথে নিজেই এটি মার্জ করতে হবে। দস্তাবেজগুলি পড়ুন ।

যতক্ষণ না আপনি নির্ধারণ করে ফেলেছেন যে আপনার পারফরম্যান্স সমস্যা আছে ততক্ষণ অতিরিক্ত রেন্ডারদের কল করার বিষয়ে আমি খুব বেশি চিন্তা করব না। রেন্ডারিং (প্রতিক্রিয়া প্রসঙ্গে) এবং আসল ডিওমে ভার্চুয়াল ডোম আপডেটগুলি করা বিভিন্ন বিষয়। এখানে রেন্ডারিংটি ভার্চুয়াল ডিওএম উত্পাদন করার কথা উল্লেখ করছে এবং ব্রাউজার ডিওএম আপডেট করার বিষয়ে নয়। প্রতিক্রিয়া setStateকলগুলি ব্যাচ করতে পারে এবং চূড়ান্ত নতুন রাষ্ট্রের সাথে ব্রাউজার ডিওএম আপডেট করতে পারে ।

const {useState, useEffect} = React;

function App() {
  const [userRequest, setUserRequest] = useState({
    loading: false,
    user: null,
  });

  useEffect(() => {
    // Note that this replaces the entire object and deletes user key!
    setUserRequest({ loading: true });
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        setUserRequest({
          loading: false,
          user: data.results[0],
        });
      });
  }, []);

  const { loading, user } = userRequest;

  return (
    <div>
      {loading && 'Loading...'}
      {user && user.name.first}
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>

বিকল্প - আপনার নিজের রাজ্যের মার্জার হুক লিখুন

const {useState, useEffect} = React;

function useMergeState(initialState) {
  const [state, setState] = useState(initialState);
  const setMergedState = newState => 
    setState(prevState => Object.assign({}, prevState, newState)
  );
  return [state, setMergedState];
}

function App() {
  const [userRequest, setUserRequest] = useMergeState({
    loading: false,
    user: null,
  });

  useEffect(() => {
    setUserRequest({ loading: true });
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        setUserRequest({
          loading: false,
          user: data.results[0],
        });
      });
  }, []);

  const { loading, user } = userRequest;

  return (
    <div>
      {loading && 'Loading...'}
      {user && user.name.first}
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>


4
আমি সম্মত হই যে এটি আদর্শ নয়, তবে এটি এটি করার একটি যুক্তিসঙ্গত উপায়। আপনি নিজের কাস্টম হুকটি লিখতে পারেন যা মার্জ করে তা যদি আপনি নিজে মার্জ করতে না চান। আমি পরিষ্কার হতে শেষ বাক্য আপডেট। প্রতিক্রিয়া কীভাবে কাজ করে আপনি তার সাথে পরিচিত? যদি তা না হয় তবে এখানে কয়েকটি লিঙ্ক রয়েছে যা আপনাকে আরও ভালভাবে বুঝতে সহায়তা করতে পারে - reactjs.org/docs/reconcistance.html , blog.vjeux.com/2013/javascript/react-performance.html
ইয়াংশুন তায়ে

4
@ জোনহবস আমি একটি বিকল্প উত্তর যুক্ত করেছি যা একটি useMergeStateহুক তৈরি করে যাতে আপনি স্বয়ংক্রিয়ভাবে রাষ্ট্রটি মার্জ করতে পারেন।
ইয়াংশুন তায়ে

4
চমত্কার, আবার ধন্যবাদ। আমি প্রতিক্রিয়া ব্যবহারের সাথে আরও পরিচিত হয়ে উঠছি তবে এর অভ্যন্তরীণ সম্পর্কে অনেক কিছু জানতে পারি। আমি তাদের একটি পড়া দিতে হবে।
jonhobbs

4
কৌতূহল কি পরিস্থিতিতে এটি সত্য হতে পারে ?: " কলগুলি ব্যাচ করতে পারে setStateএবং চূড়ান্ত নতুন রাষ্ট্রের সাথে ব্রাউজার
ডিওএম

4
ভাল .. আমি রাজ্যের সংমিশ্রণ, বা পৃথক পে-লোডের রাজ্যটি ব্যবহার করার এবং অন্য রাজ্যগুলিকে পে-লোড অবজেক্ট থেকে পড়ার বিষয়ে ভাবলাম। যদিও খুব ভাল পোস্ট। 10/10 আবার পড়বে
অ্যান্টনি মুন বিম টুরি

59

এটি ব্যবহার করে আরও একটি সমাধান রয়েছে useReducer! প্রথমে আমরা আমাদের নতুনকে সংজ্ঞায়িত করি setState

const [state, setState] = useReducer(
  (state, newState) => ({...state, ...newState}),
  {loading: true, data: null, something: ''}
)

যে আমরা কেবল ভাল পুরানো শ্রেণীর মত এটি ব্যবহার করতে পারেন পরে this.setState, শুধুমাত্র ছাড়া this!

setState({loading: false, data: test.data.results})

আপনি যেমন আমাদের নতুনটিতে লক্ষ্য করতে পারেন setState(ঠিক যেমনটি আমরা আগে this.setStateছিলাম) ঠিক তেমন আমাদের সমস্ত রাজ্য একসাথে আপডেট করার দরকার নেই! উদাহরণস্বরূপ, আমি আমাদের রাজ্যগুলির একটিতে এটির মতো পরিবর্তন করতে পারি (এবং এটি অন্যান্য রাজ্যের কোনও পরিবর্তন করে না!):

setState({loading: false})

দুর্দান্ত, হা ?!

সুতরাং আসুন সমস্ত টুকরা একসাথে রাখি:

import {useReducer} from 'react'

const getData = url => {
  const [state, setState] = useReducer(
    (state, newState) => ({...state, ...newState}),
    {loading: true, data: null}
  )

  useEffect(async () => {
    const test = await api.get('/people')
    if(test.ok){
      setState({loading: false, data: test.data.results})
    }
  }, [])

  return state
}

Expected 0 arguments, but got 1.ts(2554)এমন একটি ত্রুটি যা আমি সেইভাবে ব্যবহারকারীর পুনরায় ব্যবহার করার চেষ্টা করেছিলাম। আরও স্পষ্টভাবে setState। আপনি কিভাবে এটি ঠিক করবেন? ফাইলটি জেএস, তবে আমরা এখনও টিএস চেক ব্যবহার করি।
জেনভেন্টজি

দুর্দান্ত ভাই, ধন্যবাদ
নিশার্গ শাহ

আমি মনে করি এটি এখনও একই ধারণা যে দুটি রাজ্য একত্রিত হয়েছে। তবে কিছু ক্ষেত্রে, আপনি রাজ্যগুলিকে একীভূত করতে পারবেন না।
ব্যবহারকারী 1888955

43

প্রতিক্রিয়া-হুকগুলিতে ব্যাচিংয়ের আপডেট https://github.com/facebook/react/issues/14259

প্রতিক্রিয়া-ভিত্তিক ইভেন্টের মধ্যে থেকে বাটন ক্লিক বা ইনপুট পরিবর্তনের মতো ট্রিগার করা হলে বর্তমানে রাষ্ট্রীয় আপডেটগুলি ব্যাচ করবে। এটি ব্যাচ আপডেটগুলি যদি অ্যাসিঙ্ক কলের মতো প্রতিক্রিয়া ইভেন্ট হ্যান্ডলারের বাইরে ট্রিগার করা হয় তবে তা তা করবে না।


4
এটি এমন একটি সহায়ক উত্তর, কারণ এটি কিছু না করেই সম্ভবত বেশিরভাগ ক্ষেত্রে সমস্যার সমাধান করে। ধন্যবাদ!
davnicwil


4

যদি আপনি তৃতীয় পক্ষের হুক ব্যবহার করছেন এবং রাষ্ট্রকে একটি বস্তু বা ব্যবহারে মার্জ করতে না পারেন useReducer, তবে সমাধানটি হ'ল:

ReactDOM.unstable_batchedUpdates(() => { ... })

এখানে ড্যান আব্রামভ প্রস্তাবিত

এই উদাহরণটি দেখুন


প্রায় এক বছর পরে এই বৈশিষ্ট্যটি এখনও পুরোপুরি বিনা প্রমাণিত এবং এখনও অস্থির হিসাবে চিহ্নিত হয়েছে :-(
অ্যান্ডি

4

প্রতিলিপি নির্মাণের জন্য this.setStateবর্গ উপাদানগুলো থেকে একত্রীকরণ আচরণ, প্রতিক্রিয়া ডক্স সুপারিশ কার্যকরী ফর্ম ব্যবহার করার জন্য useStateবস্তু বিস্তার সাথে - কোন প্রয়োজন জন্য useReducer:

setState(prevState => {
  return {...prevState, loading, data};
});

দুটি রাজ্য এখন এক হিসাবে একীভূত হয়েছে, যা আপনাকে রেন্ডার চক্র সংরক্ষণ করবে।

সেখানে এক রাজ্যের বস্তুর সঙ্গে অন্য সুবিধা হল: loadingএবং dataহয় নির্ভরশীল যুক্তরাষ্ট্র। রাষ্ট্রকে একত্র করা হলে অবৈধ রাষ্ট্র পরিবর্তনগুলি আরও স্পষ্ট হয়:

setState({ loading: true, data }); // ups... loading, but we already set data

আপনি আরও ভাল করতে পারবেন তা নিশ্চিত সামঞ্জস্যপূর্ণ রাজ্যের 1. দ্বারা) উপার্জন অবস্থা - loading, success, error, ইত্যাদি - স্পষ্ট আপনার রাষ্ট্র এবং 2.) ব্যবহার করে useReducerএকটি হ্রাসকারক মধ্যে encapsulate রাষ্ট্র যুক্তিবিজ্ঞান করুন:

const useData = () => {
  const [state, dispatch] = useReducer(reducer, /*...*/);

  useEffect(() => {
    api.get('/people').then(test => {
      if (test.ok) dispatch(["success", test.data.results]);
    });
  }, []);
};

const reducer = (state, [status, payload]) => {
  if (status === "success") return { ...state, data: payload, status };
  // keep state consistent, e.g. reset data, if loading
  else if (status === "loading") return { ...state, data: undefined, status };
  return state;
};

const App = () => {
  const { data, status } = useData();
  return status === "loading" ? <div> Loading... </div> : (
    // success, display data 
  )
}

দ্রষ্টব্য: নিশ্চিত করুন উপসর্গ সাথে কাস্টম হুক্স use( useDataপরিবর্তে getData)। এছাড়াও পাস করা কলব্যাক হতে useEffectপারে না async


4
উর্ধ্বে! এটি প্রতিক্রিয়া এপিআই-তে আমি খুঁজে পাওয়া সবচেয়ে সহায়ক বৈশিষ্ট্যগুলির মধ্যে একটি। আমি যখন একটি রাষ্ট্র হিসাবে কোনও ফাংশন সংরক্ষণ করার চেষ্টা করেছি তখন এটি পাওয়া গিয়েছিল (আমি জানি এটি ফাংশনগুলি সঞ্চয় করা অদ্ভুত, তবে কোনও কারণে আমার মনে নেই) এবং এই সংরক্ষিত কল স্বাক্ষরের কারণে এটি প্রত্যাশার মতো কাজ করে নি
এলকুইমিস্টা

1

Https://stackoverflow.com/a/53575023/121143 উত্তর দেওয়ার জন্য একটি সামান্য সংযোজন

কুল! যারা এই হুক ব্যবহারের পরিকল্পনা করছেন তাদের পক্ষে এটি যুক্তি হিসাবে ফাংশন নিয়ে কাজ করার জন্য কিছুটা শক্ত ভাবে লেখা যেতে পারে যেমন:

const useMergedState = initial => {
  const [state, setState] = React.useState(initial);
  const setMergedState = newState =>
    typeof newState == "function"
      ? setState(prevState => ({ ...prevState, ...newState(prevState) }))
      : setState(prevState => ({ ...prevState, ...newState }));
  return [state, setMergedState];
};

আপডেট : অনুকূলিত সংস্করণ, আগত আংশিক অবস্থা পরিবর্তন করা হয়নি তখন রাষ্ট্র পরিবর্তন করা হবে না।

const shallowPartialCompare = (obj, partialObj) =>
  Object.keys(partialObj).every(
    key =>
      obj.hasOwnProperty(key) &&
      obj[key] === partialObj[key]
  );

const useMergedState = initial => {
  const [state, setState] = React.useState(initial);
  const setMergedState = newIncomingState =>
    setState(prevState => {
      const newState =
        typeof newIncomingState == "function"
          ? newIncomingState(prevState)
          : newIncomingState;
      return shallowPartialCompare(prevState, newState)
        ? prevState
        : { ...prevState, ...newState };
    });
  return [state, setMergedState];
};

কুল! আমি শুধু মোড়ানো হবে setMergedStateসঙ্গে useMemo(() => setMergedState, []), কারণ যেমন ডক্স বলছি, setStateপুনরায় উপস্থাপনা মধ্যে অদলবদল করে না: React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.এই ভাবে setState ফাংশন উপর recreated করা হয় না, পুনরায় রেন্ডার করে।
টোনিক্স

0

আপনি useEffectএকটি রাষ্ট্র পরিবর্তন সনাক্ত করতে এবং সেই অনুযায়ী অন্যান্য রাষ্ট্রীয় মান আপডেট করতেও ব্যবহার করতে পারেন

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