প্রতিক্রিয়া ব্যবহার করুনএফেক্ট হুক প্রাথমিক রেন্ডারে চালিত হয় না


95

ডক্স অনুসারে:

componentDidUpdate()আপডেট হওয়ার সাথে সাথেই অনুরোধ করা হয়। এই পদ্ধতিটি প্রাথমিক রেন্ডার জন্য ডাকা হয় না।

আমরা useEffect()অনুকরণ করতে নতুন হুক ব্যবহার করতে পারি componentDidUpdate(), তবে মনে useEffect()হয় প্রতিটি রেন্ডারের পরে এমনকি প্রথমবারের মতো এটি চলছে। আমি কীভাবে এটি প্রাথমিক রেন্ডারে না চালাতে পারি?

আপনি নীচের উদাহরণে দেখতে পাচ্ছেন, componentDidUpdateFunctionপ্রাথমিক রেন্ডার componentDidUpdateClassচলাকালীন মুদ্রিত হয় তবে প্রাথমিক রেন্ডার সময় মুদ্রিত হয়নি।

function ComponentDidUpdateFunction() {
  const [count, setCount] = React.useState(0);
  React.useEffect(() => {
    console.log("componentDidUpdateFunction");
  });

  return (
    <div>
      <p>componentDidUpdateFunction: {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

class ComponentDidUpdateClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  componentDidUpdate() {
    console.log("componentDidUpdateClass");
  }

  render() {
    return (
      <div>
        <p>componentDidUpdateClass: {this.state.count} times</p>
        <button
          onClick={() => {
            this.setState({ count: this.state.count + 1 });
          }}
        >
          Click Me
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <div>
    <ComponentDidUpdateFunction />
    <ComponentDidUpdateClass />
  </div>,
  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
আমি কি জিজ্ঞাসা করতে পারি যখন রেন্ডার সংখ্যার উপর ভিত্তি করে কিছু করার মতো বোধগম্য হয় এবং স্পষ্ট রাষ্ট্রের মতো পরিবর্তনশীল না হয় count?
এপ্রিলিয়ন

উত্তর:


110

আমরা useRefআমাদের পছন্দ মতো কোনও পরিবর্তনীয় মান সংরক্ষণ করতে হুক ব্যবহার করতে পারি , তাই আমরা প্রথমবার useEffectফাংশনটি চালু হচ্ছে কিনা তা ট্র্যাক রাখতে এটি ব্যবহার করতে পারি ।

যদি আমরা প্রভাবটি একই ধাপে চালিত করতে চাই তবে componentDidUpdateআমরা তার useLayoutEffectপরিবর্তে ব্যবহার করতে পারি ।

উদাহরণ

const { useState, useRef, useLayoutEffect } = React;

function ComponentDidUpdateFunction() {
  const [count, setCount] = useState(0);

  const firstUpdate = useRef(true);
  useLayoutEffect(() => {
    if (firstUpdate.current) {
      firstUpdate.current = false;
      return;
    }

    console.log("componentDidUpdateFunction");
  });

  return (
    <div>
      <p>componentDidUpdateFunction: {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

ReactDOM.render(
  <ComponentDidUpdateFunction />,
  document.getElementById("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
আমি প্রতিস্থাপন করার চেষ্টা useRefসঙ্গে useState, কিন্তু সেটার ব্যবহার আলোড়ন পুনরায় রেন্ডার, যা ঘটছে না হয় যখন থেকে বরাদ্দ firstUpdate.currentতাই আমি অনুমান এই শুধুমাত্র চমৎকার উপায় :)
Aprillion

4
কেউ যদি ব্যাখ্যা করতে পারে যে আমরা যদি ডিওএমটির পরিবর্তন বা মাপ না নিই তবে কেন লেআউট প্রভাব ব্যবহার করবে?
জেনভেন্টজি

4
@ জেনভেন্তজি এই উদাহরণে এটি প্রয়োজনীয় নয়, তবে প্রশ্নটি ছিল কীভাবে componentDidUpdateহুক দিয়ে নকল করবেন, তাই আমি এটি ব্যবহার করেছি।
থোলে

আমি এই উত্তরের উপর ভিত্তি করে এখানে একটি কাস্টম হুক তৈরি করেছি। বাস্তবায়নের জন্য ধন্যবাদ!
প্যাট্রিক রবার্টস

56

আপনি এটিকে কাস্টম হুকগুলিতে রূপান্তর করতে পারেন:

import React, { useEffect, useRef } from 'react';

const useDidMountEffect = (func, deps) => {
    const didMount = useRef(false);

    useEffect(() => {
        if (didMount.current) func();
        else didMount.current = true;
    }, deps);
}

export default useDidMountEffect;

ব্যবহারের উদাহরণ:

import React, { useState, useEffect } from 'react';

import useDidMountEffect from '../path/to/useDidMountEffect';

const MyComponent = (props) => {    
    const [state, setState] = useState({
        key: false
    });    

    useEffect(() => {
        // you know what is this, don't you?
    }, []);

    useDidMountEffect(() => {
        // react please run me if 'key' changes, but not on initial render
    }, [state.key]);    

    return (
        <div>
             ...
        </div>
    );
}
// ...

4
এই পদ্ধতির সতর্কবাণী ছুঁড়েছে যে নির্ভরতা তালিকাটি অ্যারে আক্ষরিক নয়।
theprogrammer

4
আমি এই প্রকল্পগুলি আমার প্রকল্পগুলিতে ব্যবহার করি এবং আমি কোনও সতর্কতা দেখি না, আপনি আরও তথ্য সরবরাহ করতে পারেন?
মেহেদী দেহঘানি

4
@vsync আপনি অন্যরকম কেস নিয়ে ভাবছেন যেখানে আপনি প্রাথমিক রেন্ডারে একবার প্রভাব প্রয়োগ করতে চান এবং আর কখনও হবে না
প্রোগ্রামিং গাই

4
@vsync এর নোট বিভাগে reactjs.org/docs/... এটা বিশেষভাবে বলেন, "আপনি একটি প্রভাব চালান এবং শুধুমাত্র একবার এটা পরিষ্কার আপ (মাউন্ট এবং আনমাউন্ট দিকে) করতে চান তাহলে, আপনি একটি খালি অ্যারে পাস করতে পারেন ([]) একটি হিসাবে দ্বিতীয় যুক্তি। " এটি আমার জন্য পর্যবেক্ষণ আচরণের সাথে মেলে।
প্রোগ্রামিং গাই

5

useFirstRenderফর্ম ইনপুটকে ফোকাস করার মতো কেসগুলি পরিচালনা করার জন্য আমি একটি সাধারণ হুক তৈরি করেছি :

import { useRef, useEffect } from 'react';

export function useFirstRender() {
  const firstRender = useRef(true);

  useEffect(() => {
    firstRender.current = false;
  }, []);

  return firstRender.current;
}

এটি শুরু হয়ে যায় true, তারপরে স্যুইচ falseকরে useEffect, যা কেবল একবার চালিত হয় এবং আর কখনও হয় না।

আপনার উপাদানগুলিতে, এটি ব্যবহার করুন:

const firstRender = useFirstRender();
const phoneNumberRef = useRef(null);

useEffect(() => {
  if (firstRender || errors.phoneNumber) {
    phoneNumberRef.current.focus();
  }
}, [firstRender, errors.phoneNumber]);

আপনার ক্ষেত্রে, আপনি কেবল ব্যবহার করতে হবে if (!firstRender) { ...


3

@ আরভি, আপনার পাস-ইন আনমাউন্ট ফাংশনটি কল করবেন না। এখানে আরও একটি সম্পূর্ণ সংস্করণ রয়েছে:

/**
 * Identical to React.useEffect, except that it never runs on mount. This is
 * the equivalent of the componentDidUpdate lifecycle function.
 *
 * @param {function:function} effect - A useEffect effect.
 * @param {array} [dependencies] - useEffect dependency list.
 */
export const useEffectExceptOnMount = (effect, dependencies) => {
  const mounted = React.useRef(false);
  React.useEffect(() => {
    if (mounted.current) {
      const unmount = effect();
      return () => unmount && unmount();
    } else {
      mounted.current = true;
    }
  }, dependencies);

  // Reset on unmount for the next mount.
  React.useEffect(() => {
    return () => mounted.current = false;
  }, []);
};


হ্যালো @ হোয়াটব্রিন, অ-অবনতি তালিকাটি পাস করার ক্ষেত্রে এই কাস্টম হুকটি কীভাবে ব্যবহার করবেন? খালি নয় যা উপাদান useEffect(() => {...});
ডিডমাউন্টের

4
@ কেভিডিং dependenciesআপনি যখন ফোন করবেন তখন প্যারামিটার বাদ দেওয়ার মতোই সহজ হওয়া উচিত ।
হোয়াটব্রেন

1

@ মেহেদীদেহঘানি, আপনার সমাধানটি পুরোপুরি সূক্ষ্মভাবে কাজ করবে, আপনাকে আরও একটি কাজ আনমাউন্ট করতে হবে, didMount.currentমানটি পুনরায় সেট করতে হবে false। এই কাস্টম হুক অন্য কোথাও ব্যবহার করার চেষ্টা করার সময়, আপনি ক্যাশের মান পাবেন না।

import React, { useEffect, useRef } from 'react';

const useDidMountEffect = (func, deps) => {
    const didMount = useRef(false);

    useEffect(() => {
        let unmount;
        if (didMount.current) unmount = func();
        else didMount.current = true;

        return () => {
            didMount.current = false;
            unmount && unmount();
        }
    }, deps);
}

export default useDidMountEffect;

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