React: Bug: terlalu sulit untuk diperbaiki "Tidak dapat memperbarui komponen dari dalam badan fungsi komponen yang berbeda."

Dibuat pada 28 Feb 2020  ·  109Komentar  ·  Sumber: facebook/react

# Catatan: React 16.13.1 memperbaiki beberapa kasus di mana ini overfiring. Jika memutakhirkan React dan ReactDOM ke 16.13.1 tidak memperbaiki peringatan, baca ini: https://github.com/facebook/react/issues/18178#issuecomment -595846312

Versi reaksi:

16.13.0

Langkah-langkah Untuk Mereproduksi

  1. Bangun mesin waktu.
  2. Menuju tahun 2017.
  3. Bangun aplikasi besar dengan 10 ribu baris kode.
  4. Dapatkan 80 (!) dependensi pada file package.json termasuk yang tidak lagi dipertahankan.
  5. Perbarui React ke versi terbaru pada 27 Februari 2020.
  6. Dapatkan banyak kesalahan yang Anda tidak tahu cara memperbaikinya.
  7. Beri tahu klien Anda bahwa perbaikan akan memakan waktu yang tidak diketahui dan akan memakan biaya $$$ + hari atau minggu penyelidikan atau kita akan terjebak dengan versi lama dari React dan perpustakaan terkait selamanya yang akan menghabiskan lebih banyak $$$ tapi nanti.

Serius, bisnis tempat saya bekerja tidak tertarik sama sekali. Jelas saya tidak pernah membuat peringatan seperti itu muncul jika saya mendapatkannya lebih awal. Saat ini sangat sulit untuk membuat kesalahan diperbaiki karena saya mendapatkannya di banyak kasus yang berbeda dan dengan jejak tumpukan yang besar. Saya mencoba memperbaiki setidaknya satu kesalahan yang muncul dan itu sudah memakan banyak waktu. Saya mencoba men-debug beberapa perpustakaan bekas tetapi tidak berhasil.

Hanya satu contoh:

image

Di sana kita dapat melihat penggunaan react-router yang sudah ketinggalan zaman, redux-connect yang sudah ketinggalan zaman (yang harus saya masukkan ke sumber proyek untuk memperbaiki kesalahan metode componentWillReceiveProps sudah ketinggalan zaman), beberapa HOC yang dibuat oleh recompose dll. bukan hanya pohon DOM virtual sederhana di mana saya bisa berjalan melalui komponen yang dikembangkan oleh saya dan mencari dengan setState string untuk memperbaiki bug, itu jauh lebih rumit dari itu.

Harap buat opsi "TIDAK AMAN" untuk menonaktifkan kesalahan ini atau berikan cara yang lebih sederhana untuk menemukan di mana kesalahan itu dilemparkan 🙏

Discussion

Komentar yang paling membantu

Untuk komentator masa depan. Saya mengerti melihat peringatan baru membuat frustrasi. Tapi ini menunjukkan masalah sah yang mungkin menyebabkan bug dalam kode Anda . Kami akan sangat menghargai jika Anda dapat menahan diri untuk tidak mengungkapkan sarkasme dan kekesalan.

Jika Anda tidak dapat memahami dari mana asalnya dalam jejak tumpukan, harap kirimkan tangkapan layar atau buat kotak pasir yang direproduksi dan kami akan mencoba membantu. Sebagian besar dari ini mungkin berasal dari beberapa perpustakaan, jadi hal yang paling produktif untuk dilakukan adalah mengurangi kasus ini dan kemudian mengajukan masalah dengan perpustakaan tersebut.

Terima kasih semua.

Semua 109 komentar

Saya berharap kami telah menambahkan peringatan ini sebelumnya. Maaf kami tidak melakukannya. Itu adalah kekeliruan dengan diperkenalkannya Hooks. Saya percaya ini pasti disebabkan oleh kode yang lebih baru yang menggunakan Hooks, karena sudah ada peringatan yang sama untuk kelas jauh lebih awal sehingga kode sebelumnya akan sudah melihat peringatan ini.

Perhatikan bahwa ini kemungkinan akan mulai gagal di versi React yang akan datang. Disengaja atau sebaliknya (kami memiliki banyak bug dengan pola ini). Jadi terlepas dari itu, Anda mungkin akan terjebak pada versi lama. Jika tidak mungkin untuk memperbaikinya, saya sarankan menyematkan ke versi React yang lebih lama.

Namun demikian, kami ingin membantu Anda dan membuatnya semudah mungkin untuk memperbaikinya, termasuk melihat apakah kami bisa mendapatkan bantuan dari penulis perpustakaan untuk menerbitkan versi tetap.

Jika Anda memperluas panah > di konsol Chrome, Anda juga akan melihat jejak Stack tambahan (selain tumpukan Komponen di tangkapan layar Anda). Bisakah Anda memposting itu juga? Itu akan menunjukkan kepada Anda situs panggilan yang tepat yang menyebabkan efek samping dalam render.

Dengan saya ini muncul ketika saya menggunakan formik

image

@sebmarkbage terima kasih atas tanggapannya. Stacktrace yang muncul setelah mengklik > itu konyol. Ini berisi 200+ item!

Saya akan menempelkannya di sana atau memberikan tautan ke pastebin tetapi mencoba arah yang berbeda. Saya berjalan melalui masalah Github dari beberapa perpustakaan bekas dan menemukan bahwa salah satu tersangka adalah redux-form: https://github.com/redux-form/redux-form/issues/4619 . Saya harap itu satu-satunya perpustakaan yang menyebabkan kesalahan dan saya akan menunggu perbaikan sebelum memutakhirkan Bereaksi.

Tapi tetap saja, saya akan meminta untuk tidak menutup masalah ini dan saya mengusulkan pengembang lain untuk menyebutkan di sini perpustakaan yang juga menyebabkan kesalahan ini.

@RodolfoSilva apakah Anda yakin itu disebabkan oleh formik? Jika ya, bisakah Anda membuat masalah di sana dan memposting tautan ke sini? Saya akan membuat daftar masalah seperti itu di awal pesan pertama saya jika daftar tersebut akan berisi lebih dari satu item.

Ini benar-benar perlu ditangani secepatnya. Itu membuat peningkatan menjadi tidak mungkin. Jejak kesalahan hampir tidak mungkin.

Hm. Saya ingin tahu apakah menjelaskan baris mana yang harus dicari dalam pesan kesalahan akan membantu.

Dalam hal ini baris pertama yang harus dicari adalah baris setelah dispatchAction . Itu harus menjadi hal yang memanggil React.

@RodolfoSilva dapatkah Anda memposting sumber FormItemInput.js , jika itu sesuatu yang dapat Anda bagikan? Itu sepertinya memanggil pengiriman atau setState pada baris 71.

Saya pikir sangat penting bahwa pesan kesalahan ini dimodifikasi untuk memasukkan dengan tepat baris kode mana yang menyebabkan kesalahan. Hampir tidak mungkin untuk menentukan apakah itu kode lokal atau kode perpustakaan yang menyebabkan masalah. Pustaka perpustakaan eksternal

Saya cukup yakin bahwa React-Redux tidak bersalah di sini, tetapi setuju bahwa pesan kesalahan dan tumpukan komponen membuat agak sulit untuk mengetahui apa yang sebenarnya terjadi.

Saya menghadapi masalah yang sama dengan bentuk Redux!

Saya memiliki masalah yang sama dan saya melihat bahwa peringatan itu muncul pertama kali saya menulis di bidang redux saya atau ketika saya menghapus semuanya

Saya punya masalah itu juga, ini komponen saya:

`const [harga, setPrice] = useState(0);

const updatePrice = (Harga baru) => {
setHarga(Harga baru)
}
< CardContainer onPriceUpdated={updatePrice} > CardContainer >
`

Jadi, dalam hal ini komponen CardContainer saya, beri tahu komponen induk ketika harga diperbarui dan komponen induk merender pangeran baru.
Jadi saya kira React memperingatkan saya bahwa saya mencoba memperbarui status komponen menggunakan fungsi komponen lain.
Saya baru dalam reaksi, jadi saya tidak yakin apakah ini pola Bereaksi atau sebenarnya bug.

Jika kalian memiliki saran untuk mengatasi peringatan ini, saya akan sangat menghargainya``

@l0gicgate

Saya pikir sangat penting bahwa pesan kesalahan ini dimodifikasi untuk memasukkan dengan tepat baris kode mana yang menyebabkan kesalahan.

Ada batasan untuk apa yang dapat kita lakukan dalam JavaScript. Tetapi semua informasi ada di tumpukan yang Anda lihat di browser . Yang perlu Anda lakukan adalah melewati baris yang ada di dalam React.

Untuk melihat tumpukan JavaScript, Anda perlu mengklik panah kecil di sebelah pesan kesalahan .

Misalnya, lihat tangkapan layar ini dari sebelumnya:

75614021-cb812980-5b12-11ea-8a6e-a38f4cd6aeef

Saya menghargai itu agak menjengkelkan untuk menggali melalui tumpukan, tetapi seharusnya tidak terlalu sulit untuk melewati beberapa frame pertama. Bingkai berikutnya adalah sumber masalahnya. Dalam hal ini, sepertinya ada sesuatu di dalam perpustakaan Formik. Jadi Anda dapat mengajukan masalah dengannya.

@martinezwilmer Contoh ini terlalu kecil untuk membantu. Tolong buat kotak pasir.

Untuk komentator masa depan. Saya mengerti melihat peringatan baru membuat frustrasi. Tapi ini menunjukkan masalah sah yang mungkin menyebabkan bug dalam kode Anda . Kami akan sangat menghargai jika Anda dapat menahan diri untuk tidak mengungkapkan sarkasme dan kekesalan.

Jika Anda tidak dapat memahami dari mana asalnya dalam jejak tumpukan, harap kirimkan tangkapan layar atau buat kotak pasir yang direproduksi dan kami akan mencoba membantu. Sebagian besar dari ini mungkin berasal dari beberapa perpustakaan, jadi hal yang paling produktif untuk dilakukan adalah mengurangi kasus ini dan kemudian mengajukan masalah dengan perpustakaan tersebut.

Terima kasih semua.

Sulit untuk menemukan garis yang tepat terkait dengan peringatan di sini:

@gaearon apakah Anda punya satu tip lagi tentang itu?

image

Sulit untuk menemukan garis yang tepat terkait dengan peringatan di sini:

Apa yang membuatnya sulit? Seperti yang saya sebutkan di atas, ini adalah baris pertama yang tidak mengatakan "bereaksi" di sisi kanan. Dalam hal ini, connectAdvanced dari Redux. Silakan ajukan masalah di React Redux sehingga pengelola memiliki kesempatan untuk melihatnya.

Seperti yang saya katakan di atas, saya akan _sangat_ terkejut jika apa pun yang terjadi di sini adalah masalah dengan React-Redux.

Yang mengatakan, saya juga tidak yakin persis apa yang memicu pesan ini di tempat pertama. Saya setengah mengerti apa yang dikatakan pesan kesalahan, tetapi pola kode aplikasi seperti apa yang sebenarnya menjadi contoh perilaku itu?

Saya mengalami ini baru-baru ini dan perbaikannya membungkus situs panggilan penangan setState di useEffect , seperti ini: https://github.com/airbnb/lunar/commit/db08613d46ea21089ead3e7b5cfff995f15c69a7#diff -1c3bdd397b1ce506414R2456888 ( onChange dan onSubmit menggunakan setState lebih tinggi dari rantai).

@martinezwilmer Di mana onPriceUpdated dipanggil? Mungkin coba membungkusnya dengan useEffect ?

Masalah yang sama tampaknya terjadi untuk urql

Kami menggunakan use-subscription + wonka (untuk streaming) untuk mengatur pembaruan kami, namun pembaruan dapat masuk secara serempak. Di sini kita telah mengambil todos jadi jika kita mengklik tombol Open hasilnya akan langsung muncul, namun ini tampaknya memicu kesalahan berikut.

image

Dalam kasus kami ini dimaksudkan, kami tidak bisa hanya menampilkan fetching: true untuk hasil sinkronisasi, yang akan menghasilkan antarmuka gelisah.

Ini mulai muncul semakin banyak di perpustakaan pihak ketiga sekarang: urql , Apollo .

Saya mengalami ini dan selama beberapa jam saya berasumsi masalahnya ada di kode saya. Stacktrace yang diringkas menunjuk ke komponen saya, dan bukan hal yang aneh bagi saya untuk melihat perpustakaan pihak ketiga di stacktrace yang diperluas ketika saya _melakukan_ secara eksplisit memicu kesalahan. Dari penelitian saya (walaupun terbatas) tentang peringatan khusus ini, tampaknya sebagian besar pengembang tidak menyebabkan masalah ini sendiri, melainkan bergantung pada kode yang menyebabkannya. Biasanya merupakan praktik yang baik untuk menganggap itu bukan bug di hulu tetapi ketika itu adalah bug hulu, membuang-buang waktu untuk melacak masalah dalam kode Anda yang tidak ada agak membuat frustrasi. Apakah ada yang dapat dilakukan React untuk membantu rata-rata pengguna menentukan apakah itu kode yang mereka tulis, atau kode yang mereka andalkan yang menyebabkan masalah?

Satu hal yang saya perhatikan dari masalah Apollo adalah:

Stacktrace peringatan menunjukkan komponen yang menginisialisasi perubahan, bukan komponen yang dirender ulang oleh perubahan tersebut

Jika ini benar, dapatkah React memberikan informasi lebih lanjut di sini? Mungkin memberi tahu kami komponen awal dan komponen yang menyebabkannya diperbarui?

Seperti @hugo , saya menemukan ini saat menguji aplikasi Ionic baru :

  1. npx ionic start demo sidemenu --type=react
  2. react-scripts test

Memang, penyebabnya terkubur di tengah dan bawah tumpukan jejak.

console.error node_modules/react-dom/cjs/react-dom.development.js:88
    Warning: Cannot update a component from inside the function body of a different component.
        in Route (at App.tsx:37)
        in View (created by StackManagerInner)
        in ViewTransitionManager (created by StackManagerInner)
        in ion-router-outlet (created by IonRouterOutlet)
        in IonRouterOutlet (created by ForwardRef(IonRouterOutlet))
        in ForwardRef(IonRouterOutlet) (created by StackManagerInner)
        in StackManagerInner (created by Context.Consumer)
        in Unknown (created by Component)
        in Component (created by ForwardRef(IonRouterOutlet))
        in ForwardRef(IonRouterOutlet) (at App.tsx:36)
        in ion-split-pane (created by IonSplitPane)
        in IonSplitPane (created by ForwardRef(IonSplitPane))
        in ForwardRef(IonSplitPane) (at App.tsx:34)
        in NavManager (created by RouteManager)
        in RouteManager (created by Context.Consumer)
        in RouteManager (created by IonReactRouter)
        in Router (created by BrowserRouter)
        in BrowserRouter (created by IonReactRouter)
        in IonReactRouter (at App.tsx:33)
        in ion-app (created by IonApp)
        in IonApp (created by ForwardRef(IonApp))
        in ForwardRef(IonApp) (at App.tsx:32)
        in App (at App.test.tsx:6)

Masalah ini adalah yang paling dekat yang bisa saya temukan mengenai masalah ini.

Kami menemukan pola khusus yang menyebabkan masalah ini dengan mobx di https://github.com/mobxjs/mobx-react/issues/846

@sebmarkbage Saya tidak bisa lagi mereproduksi masalah ini. Kami telah memperbarui beberapa perpustakaan dan masalah selesai.

@jgoux sepertinya kita menghadapi masalah yang sama @Clovis ^^ Terlihat

Saya mulai mendapatkan kesalahan ini setelah pembaruan bereaksi terhadap react 16.13.0 . Masalahnya cukup jelas karena salah satu komponen saya memperbarui yang lain setelah tindakan tertentu dilakukan. Namun, tidak yakin mengapa ini memberikan peringatan. Adakah saran tentang cara menyiasatinya?

@gaearon terima kasih atas detail tentang cara men-debug dari tumpukan, saya pribadi tidak akan dapat mengetahui dari mana kesalahan ini berasal jika Anda tidak memberikan contoh itu. 🙏

Tidak yakin apakah masalah saya terkait, saya mencoba memperbarui status komponen formulir saya tetapi setiap kali saya mencoba menambahkan penangan onChange, itu terus memberi saya kesalahan ini. Ingat, saya menggunakan react-jsonschema-form dan mengimpor komponen Form dan saya menggunakan properti onChange untuk memperbarui status..

Bagi saya, ini adalah pola yang menyebabkan masalah.

image

Mungkin ada cara untuk mengatasi ini. Tapi log konsol memang mengarahkan saya langsung ke baris 385

Saya baru bereaksi tetapi saya memiliki kode seperti ini:

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={setMobileNavOpen(false)} //problem here
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

Yang menghasilkan: Cannot update a component from inside the function body of a different component.

Yang harus saya lakukan adalah menambahkan fungsi panah ke setMobileNavOpen di MobileNav seperti:

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(false)} //fixes problem
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

Dan itu memperbaiki masalah, semoga ini bisa membantu seseorang!

Saya baru bereaksi tetapi saya memiliki kode seperti ini:

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={setMobileNavOpen(false)} //problem here
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

Yang menghasilkan: Cannot update a component from inside the function body of a different component.

Yang harus saya lakukan adalah menambahkan fungsi panah ke setMobileNavOpen di MobileNav seperti:

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(false)} //fixes problem
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

Dan itu memperbaiki masalah, semoga ini bisa membantu seseorang!

Contoh Anda sebenarnya adalah salah satu kesalahan awal yang dilakukan orang dengan reaksi. Saya tidak yakin itu masalah yang sama persis yang sedang dibahas di sini. Baris Anda di sini: onClick={setMobileNavOpen(false) memanggil fungsi selama tender tombol, bukan saat klik. Itu sebabnya membungkusnya dengan fungsi panah memperbaikinya.

Saya mengalami masalah ini dengan React Router di mana saya perlu mengirimkan tindakan Redux sebelum <Redirect> membawa pengguna ke lokasi yang berbeda. Masalahnya sepertinya pengalihan terjadi sebelum pengiriman selesai. Saya memperbaiki masalah dengan menjanjikan tindakan saya.

Sebelum:

<Route
  render={routeProps => {
    setRedirectionTarget(somePath(routeProps));
    return <Redirect to={someOtherPath} />;
  }}
/>;

function mapDispatchToProps(dispatch: ThunkDispatch) {
  return {
    setRedirectionTarget: (target: string | null) => dispatch(setRedirectionTarget(target))
  };
}

export const setRedirectionTarget = (location: string | null): SetRedirectionTarget => {
  return {
    type: SET_REDIRECTION_TARGET,
    location
  };
};

Setelah:

function mapDispatchToProps(dispatch: ThunkDispatch) {
  return {
    setRedirectionTarget: async (target: string | null) => dispatch(await setRedirectionTarget(target))
  };
}

export const setRedirectionTarget = (location: string | null): Promise<SetRedirectionTarget> => {
  return Promise.resolve({
    type: SET_REDIRECTION_TARGET,
    location
  });
};

Saya pikir itu akan membuat pesan kesalahan menyertakan nama untuk kedua komponen yang sedang dirender, dan komponen yang setStatenya dipanggil. Ada yang mau kirim PR untuk ini?

Saya pikir itu akan membuat pesan kesalahan menyertakan nama untuk kedua komponen yang sedang dirender, dan komponen yang setStatenya dipanggil. Ada yang mau kirim PR untuk ini?

Saya senang melihat ini. Adakah petunjuk yang harus saya ketahui?

@ samcooke98 Saya telah membuka PR untuk ini https://github.com/facebook/react/pull/18316

Seperti yang telah ditunjukkan orang lain, Anda dapat mengalami masalah ini jika Anda memiliki langganan di kait seperti dengan Apollo dan mencoba dan memperbarui toko saat menerima data. Perbaikan sederhana adalah dengan menggunakan useEffect misalnya

export const useOrderbookSubscription = marketId => {
  const { data, error, loading } = useSubscription(ORDERBOOK_SUBSCRIPTION, {
    variables: {
      marketId,
    },
  })

  const formattedData = useMemo(() => {
    // Don't dispatch in here.
  }, [data])

  // Don't dispatch here either

  // Dispatch in a useEffect
  useEffect(() => {
    orderbookStore.dispatch(setOrderbookData(formattedData))
  }, [formattedData])

  return { data: formattedData, error, loading }
}

Kami menghadapi masalah ini di Hyper tetapi kami tidak menggunakan kait, dan tidak dapat menemukan apa pun dalam panggilan render dari tumpukan panggilan. Tapi ada panggilan di UNSAFE_componentWillReceiveProps di tumpukan. Memperbarui itu dengan componentDidUpdate memperbaiki masalah bagi kami https://github.com/zeit/hyper/pull/4382
Diposting di sini jika mungkin membantu seseorang

Sama di sini, ada panggilan UNSAFE_componentWillMount, mengubah/menghapusnya memperbaiki masalah

tetapi kami tidak menggunakan kait, dan tidak dapat menemukan apa pun dalam panggilan render dari tumpukan panggilan

Ini terdengar aneh. Saya tidak yakin bagaimana Anda mendapatkan peringatan ini. Itu hanya menyala jika setState milik komponen fungsi. Seperti apa tumpukan Anda?

tetapi kami tidak menggunakan kait, dan tidak dapat menemukan apa pun dalam panggilan render dari tumpukan panggilan

Ini terdengar aneh. Saya tidak yakin bagaimana Anda mendapatkan peringatan ini. Itu hanya menyala jika setState milik komponen fungsi. Seperti apa tumpukan Anda?

Kelaskan komponen dengan Redux, dan fungsi yang menerapkan plugin ke komponen. Mungkin itu sebabnya dihitung sebagai komponen fungsi? Tetapi mengapa memperbarui kait siklus hidup memperbaikinya?

Saya tidak yakin. Bisakah Anda mencoba membuat contoh terisolasi di kotak pasir? Saya punya firasat Anda mungkin melakukan sesuatu yang tidak terduga.

Tidak yakin apakah saya dapat menirunya dalam contoh yang terisolasi. Saya kesulitan menemukan penyebabnya, dan baru saja memperbarui kait siklus hidup seperti yang ada di jejak tumpukan dan menunggu pembaruan. Itu entah bagaimana memperbaiki masalah.
Anda dapat melihat repo pada saat ada masalah di sini

Mungkinkah karena fakta bahwa UNSAFE_componentWillReceiveProps dan componentDidUpdate berada di dalam komponen pada saat itu (yang tidak aman salah ditinggalkan di sana)?

Saya juga mendapatkan peringatan ini, dan jejak tumpukan menunjuk ke kait setScriptLoaded (lihat di bawah untuk kait khusus saya). Jadi tampaknya meskipun saya menggunakan useEffect , React masih akan memberikan peringatan jika setState handler bersarang di dalam panggilan balik async lain (dalam kasus saya, itu bersarang di setTimeout dan pengendali acara load )? Pertama kali saya menggunakan Hooks, jadi saya akan menghargai saran apa pun. Terima kasih!

/**
 * Detect when 3rd party script is ready to use
 * 
 * <strong i="11">@param</strong> {function} verifyScriptLoaded Callback to verify if script loaded correctly
 * <strong i="12">@param</strong> {string} scriptTagId 
 */

export const useScriptLoadStatus = (verifyScriptLoaded, scriptTagId) => {
    let initLoadStatus = true; // HTML already includes script tag when rendered server-side
    if (__BROWSER__) {
        initLoadStatus = typeof verifyScriptLoaded === 'function' ? verifyScriptLoaded() : false;
    }
    const [isScriptLoaded, setScriptLoaded] = useState(initLoadStatus); 

    useEffect(() => {
        if (!isScriptLoaded) {
            // need to wrap in setTimeout because Helmet appends the script tags async-ly after component mounts (https://github.com/nfl/react-helmet/issues/146)
            setTimeout(() => {
                let newScriptTag = document.querySelector(`#${scriptTagId}`);
                if (newScriptTag && typeof verifyScriptLoaded === 'function') {
                    newScriptTag.addEventListener('load', () => { 
                        return verifyScriptLoaded() ? setScriptLoaded(true) : null;
                    });
                    // double check if script is already loaded before the event listener is added
                    return verifyScriptLoaded() ? setScriptLoaded(true) : null;
                }
            }, 100);
        }
    });

    return isScriptLoaded;
};

@LabhanshAgrawal

Mungkinkah karena fakta bahwa UNSAFE_componentWillReceiveProps dan componentDidUpdate berada di komponen pada saat itu (yang tidak aman ditinggalkan di sana secara keliru)?

Saya tidak berpikir metode siklus hidup apa pun relevan di sini sama sekali. Inilah sebabnya saya mengatakan bahwa ada sesuatu yang aneh dalam contoh Anda.

@suhanw Tolong berikan contoh lengkap di CodeSandbox. Saya tidak melihat ada masalah dengan kode Anda yang seharusnya menyebabkan peringatan ini.

@LabhanshAgrawal Bisakah Anda memposting tumpukan lengkap Anda? Saya pikir apa yang mungkin terjadi adalah UNSAFE_componentWillReceiveProps (yang setara dengan rendering) memanggil setState pada komponen lain.

backend.js:6 Warning: Cannot update a component from inside the function body of a different component.
    in Term                           (created by _exposeDecorated(Term))
    in _exposeDecorated(Term)         (created by DecoratedComponent)
    in DecoratedComponent             (created by TermGroup_)
    in TermGroup_                     (created by ConnectFunction)
    in ConnectFunction                (created by Connect(TermGroup_))
    in Connect(TermGroup_)            (created by _exposeDecorated(TermGroup))
    in _exposeDecorated(TermGroup)    (created by DecoratedComponent)
    in DecoratedComponent             (created by Terms)
    in div                            (created by Terms)
    in div                            (created by Terms)
    in Terms                          (created by _exposeDecorated(Terms))
    in _exposeDecorated(Terms)        (created by DecoratedComponent)
    in DecoratedComponent             (created by ConnectFunction)
    in ConnectFunction                (created by Connect(DecoratedComponent))
    in Connect(DecoratedComponent)    (created by Hyper)
    in div                            (created by Hyper)
    in div                            (created by Hyper)
    in Hyper                          (created by _exposeDecorated(Hyper))
    in _exposeDecorated(Hyper)        (created by DecoratedComponent)
    in DecoratedComponent             (created by ConnectFunction)
    in ConnectFunction                (created by Connect(DecoratedComponent))
    in Connect(DecoratedComponent)
    in Provider
r                                     @ backend.js:6
printWarning                          @ react-dom.development.js:88
error                                 @ react-dom.development.js:60
warnAboutRenderPhaseUpdatesInDEV      @ react-dom.development.js:23260
scheduleUpdateOnFiber                 @ react-dom.development.js:21196
dispatchAction                        @ react-dom.development.js:15682
checkForUpdates                       @ connectAdvanced.js:88
handleChangeWrapper                   @ Subscription.js:97
(anonymous)                           @ Subscription.js:23
batchedUpdates$1                      @ react-dom.development.js:21887
notify                                @ Subscription.js:19
notifyNestedSubs                      @ Subscription.js:92
checkForUpdates                       @ connectAdvanced.js:77
handleChangeWrapper                   @ Subscription.js:97
(anonymous)                           @ Subscription.js:23
batchedUpdates$1                      @ react-dom.development.js:21887
notify                                @ Subscription.js:19
notifyNestedSubs                      @ Subscription.js:92
handleChangeWrapper                   @ Subscription.js:97
dispatch                              @ redux.js:222
e                                     @ VM64:1
(anonymous)                           @ effects.ts:11
(anonymous)                           @ write-middleware.ts:14
(anonymous)                           @ index.js:11
(anonymous)                           @ plugins.ts:538
(anonymous)                           @ plugins.ts:540
(anonymous)                           @ index.js:11
dispatch                              @ redux.js:638
(anonymous)                           @ sessions.ts:124
(anonymous)                           @ index.js:8
dispatch                              @ VM64:1
onResize                              @ terms.ts:62
(anonymous)                           @ term.js:179
e.fire                                @ xterm.js:1
t.resize                              @ xterm.js:1
e.resize                              @ xterm.js:1
e.fit                                 @ xterm-addon-fit.js:1
fitResize                             @ term.js:291
UNSAFE_componentWillReceiveProps      @ term.js:408
callComponentWillReceiveProps         @ react-dom.development.js:12998
updateClassInstance                   @ react-dom.development.js:13200
updateClassComponent                  @ react-dom.development.js:17131
beginWork                             @ react-dom.development.js:18653
beginWork$1                           @ react-dom.development.js:23210
performUnitOfWork                     @ react-dom.development.js:22185
workLoopSync                          @ react-dom.development.js:22161
performSyncWorkOnRoot                 @ react-dom.development.js:21787
(anonymous)                           @ react-dom.development.js:11111
unstable_runWithPriority              @ scheduler.development.js:653
runWithPriority$1                     @ react-dom.development.js:11061
flushSyncCallbackQueueImpl            @ react-dom.development.js:11106
flushSyncCallbackQueue                @ react-dom.development.js:11094
batchedUpdates$1                      @ react-dom.development.js:21893
notify                                @ Subscription.js:19
notifyNestedSubs                      @ Subscription.js:92
handleChangeWrapper                   @ Subscription.js:97
dispatch                              @ redux.js:222
e                                     @ VM64:1
(anonymous)                           @ effects.ts:11
(anonymous)                           @ write-middleware.ts:14
(anonymous)                           @ index.js:11
(anonymous)                           @ plugins.ts:538
(anonymous)                           @ plugins.ts:540
(anonymous)                           @ index.js:11
dispatch                              @ redux.js:638
effect                                @ ui.ts:60
(anonymous)                           @ effects.ts:13
(anonymous)                           @ write-middleware.ts:14
(anonymous)                           @ index.js:11
(anonymous)                           @ plugins.ts:538
(anonymous)                           @ plugins.ts:540
(anonymous)                           @ index.js:11
dispatch                              @ redux.js:638
(anonymous)                           @ ui.ts:54
(anonymous)                           @ index.js:8
dispatch                              @ VM64:1
(anonymous)                           @ index.tsx:162
emit                                  @ events.js:210
(anonymous)                           @ rpc.ts:31
emit                                  @ events.js:210
onMessage                             @ init.ts:50

@gaearon menghargai respon cepat. Saya akan mencari cara untuk membuat contoh di CodeSandbox (ini menantang), tetapi sementara itu, ini adalah jejak tumpukan lengkap saya

baris yang menunjuk ke kait khusus saya adalah ini: https://Gist.github.com/suhanw/bcc2688bba131df8301dae073977654f#file -stack-trace-L144

akan luar biasa jika Anda dapat melihat dan memberi tahu saya jika ada sesuatu di jejak tumpukan sebelum/sesudah kait khusus saya yang harus saya selidiki lebih lanjut. Terima kasih!

@LabhanshAgrawal Di tumpukan Anda, UNSAFE_componentWillReceiveProps memanggil beberapa fitResize yang mengirimkan tindakan Redux, yang pada gilirannya memperbarui banyak komponen. Oleh karena itu masalahnya. Jadi ya, mengubahnya menjadi componentDidUpdate berhasil.

@suhanw Di tumpukan Anda, sesuatu yang disebut ModuleImpressionTracker muncul untuk mengirimkan tindakan Redux selama konstruktor. Konstruktor seharusnya tidak memiliki efek samping. Saya pikir itu penyebab masalahnya, bukan Hook Anda.

Saya mengerti, jadi saya mengambil kesimpulan bahwa UNSAFE_componentWillReceiveProps dihitung sebagai rendering tetapi componentDidUpdate tidak.
Bisakah Anda mengklarifikasi sedikit tentang masalah dengan komponen Fungsional dan kait yang melakukan setState, tidak dapat memahaminya dengan jelas dari diskusi di atas.
Maaf jika pertanyaannya agak konyol atau pengambilan saya salah, saya agak baru dalam hal ini.

@LabhanshAgrawal

Bisakah Anda mengklarifikasi sedikit tentang masalah dengan komponen Fungsional dan kait yang melakukan setState, tidak dapat memahaminya dengan jelas dari diskusi di atas.

Sejujurnya saya sendiri tidak yakin karena semua hal Redux di tengah. Ini cukup membingungkan karena bagaimana React Redux diimplementasikan. Akan lebih baik jika seseorang bisa mendapatkan repro yang jelas yang melibatkan React saja tetapi menggunakan komponen kelas.

Sepertinya masih ada banyak jejak tumpukan yang ditempel, dan tidak banyak repro aktual dari masalah aktual apa pun jenisnya.

Saya kira yang untuk urql dimaksudkan @gaearon jadi yang terjadi adalah:

  • Kami memasang <Todos /> , yang mengambil data dan merendernya
  • Sebelumnya mengatur aliran yang terhubung ke urqlClient
  • Kami membuat <Todos /> kedua kami ini akan menghasilkan kombinasi kueri + variabel yang sama sehingga akan menyegarkan hasil untuk <Todos /> dari langkah pertama.
  • use-subscription dipicu untuk keduanya.

Repro - Tekan "buka" di bagian atas saat kueri pertama dirender.

Kami dapat mengantri pembaruan tetapi selama kami tidak melakukan render top-down dengan konteks, kami tidak akan dapat menghindari masalah ini, saya berasumsi? Secara teori, ini adalah perilaku yang dimaksudkan untuk kasus ini. Penasaran bagaimana Relay bekerja di sekitar ini.

EDIT: kami mungkin telah menemukan solusi untuk kasus urql dengan tidak memicu semua pelanggan untuk memperbarui selama getCurrentValue dari use-subscription

https://github.com/FormidableLabs/urql/commit/3a597dd92587ef852c18139e9781e853f763e930

OK jadi melihat lebih dalam ini, saya pikir kami memperingatkan dalam lebih banyak kasus (misalnya untuk kelas) dari yang kami maksudkan semula. Kami akan melihat ke dalam membungkam beberapa dari mereka.

@JoviDeCroock Contoh ini tidak terlalu membantu karena saya tidak tahu apa yang dilakukan urql . :-) Jika Anda menginginkan umpan balik tentang suatu pola, dapatkah Anda menyiapkan kotak pasir yang menunjukkan pola itu secara terpisah?

@JoviDeCroock Ya, getCurrentValue jelas tidak dimaksudkan untuk menimbulkan efek samping. Kami pikir nama itu cukup eksplisit tentang itu.

Saya memiliki masalah di mana saya mendapatkan peringatan ini ketika mengirim tindakan redux di dalam lingkup root dari custom hook.

function useCustomHook() {
   const dispatch = useDispatch()
   const value = useSomeOtherHook()
   dispatch(action(value))
}

Saya memperbaikinya dengan membungkus pengiriman dalam useEffect .

@Glinkis Kedengarannya seperti pola yang buruk. Mengapa Anda ingin memberi tahu seluruh pohon setiap kali komponen telah dirender?

@gaearon ya, masalah yang kami coba selesaikan lebih baik dijelaskan di sini dan tampaknya cukup umum.

@Glinkis Kedengarannya seperti pola yang buruk. Mengapa Anda ingin memberi tahu seluruh pohon setiap kali komponen telah dirender?

Saya perlu menyinkronkan status redux saya dengan ID untuk rute saya, jadi saya mengirimkan pembaruan ini ketika rute saya berubah.

Saya tahu ini mungkin kurang optimal, tetapi saya tidak menemukan cara lain untuk mengakses data perutean di dalam pemilih saya.

@Glinkis Dari mana data rute berasal?

@JoviDeCroock Saya pikir rekomendasi terbaru kami untuk pola itu adalah pass pengumpulan sampah terjadwal khusus.

@Glinkis Dari mana data rute berasal?

Tidak yakin apakah ini milik saya diskusi ini, tetapi ini adalah pengait saya.

export function useOpenFolder() {
  const dispatch = useDispatch()
  const match = useRouteMatch('/:workspace/(super|settings)?/:type?/:id?/(item)?/:item?')
  const { id, item } = match?.params || {}

  useEffect(() => {
    dispatch(
      openFolder({
        id: id || '',
        item: item || '',
      })
    )
  }, [dispatch, item, id])
}

Saya kemudian menggunakan status ini untuk penyeleksi seperti:

export const getActiveItem = createSelector(
  [getActiveFolderItem, getItems],
  (activeItem, items) => items.all[activeItem]
)

@Glinkis Ya mungkin mari kita selesaikan di sini, tetapi saran saya adalah membaca useRouteMatch di komponen induk, meneruskan ID sebagai prop, dan kemudian membaca props di pemilih seperti biasanya. (Saya sebenarnya tidak tahu bagaimana ini dilakukan di Redux hari ini tetapi pasti ada beberapa cara.)

Saya mengerti, jadi saya mengambil kesimpulan bahwa UNSAFE_componentWillReceiveProps dihitung sebagai rendering tetapi componentDidUpdate tidak.

@LabhanshAgrawal Itu benar. Beberapa penjelasan latar belakang di sini :

Secara konseptual, React bekerja dalam dua fase:

  • Fase render menentukan perubahan apa yang perlu dilakukan misalnya DOM. Selama fase ini, React memanggil render dan kemudian membandingkan hasilnya dengan render sebelumnya.
  • Fase komit adalah ketika React menerapkan perubahan apa pun. (Dalam kasus React DOM, ini adalah saat React menyisipkan, memperbarui, dan menghapus node DOM.) React juga memanggil siklus hidup seperti componentDidMount dan componentDidUpdate selama fase ini.

Fase komit biasanya sangat cepat, tetapi rendering bisa lambat. Untuk alasan ini, mode bersamaan yang akan datang (yang belum diaktifkan secara default) memecah pekerjaan rendering menjadi beberapa bagian, menjeda dan melanjutkan pekerjaan untuk menghindari pemblokiran browser. Ini berarti bahwa React dapat memanggil siklus hidup fase render lebih dari sekali sebelum melakukan, atau dapat memanggilnya tanpa melakukan sama sekali (karena kesalahan atau gangguan prioritas yang lebih tinggi).

Siklus hidup fase render mencakup metode komponen kelas berikut:

  • constructor
  • componentWillMount (atau UNSAFE_componentWillMount )
  • componentWillReceiveProps (atau UNSAFE_componentWillReceiveProps )
  • componentWillUpdate (atau UNSAFE_componentWillUpdate )
  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • setState fungsi updater (argumen pertama)

Karena metode di atas mungkin dipanggil lebih dari sekali, penting untuk tidak mengandung efek samping. Mengabaikan aturan ini dapat menyebabkan berbagai masalah, termasuk kebocoran memori dan status aplikasi yang tidak valid. Sayangnya, sulit untuk mendeteksi masalah ini karena sering kali bersifat non-deterministik .

OK jadi melihat lebih dalam ini, saya pikir kami memperingatkan dalam lebih banyak kasus (misalnya untuk kelas) dari yang kami maksudkan semula. Kami akan melihat ke dalam membungkam beberapa dari mereka.

Saya kira bahkan jika itu lebih dari yang dimaksudkan, menurut saya itu adalah hal yang baik untuk dimiliki karena membantu dalam meningkatkan proyek kami menggunakan kelas.

Pesan kesalahan yang cukup jelas ini "Tidak dapat memperbarui komponen dari dalam badan fungsi komponen yang berbeda"

Itu berarti mengeksekusi sebagai fungsi alih-alih memanggil komponen.

contoh seperti ini:

const App = () => {  
  const fetchRecords = () => { 
    return <div>Loading..</div>;
  };  
  return fetchRecords() // and not like <FetchRecords /> unless it is functional component.
};
export default App;

@rpateld Saya tidak berpikir contoh yang Anda tunjukkan terkait dengan peringatan ini.

https://github.com/facebook/react/pull/18330 akan menyelesaikan kasus yang tidak ingin kami mulai.

Saya juga menghadapi masalah ini dengan react@experimental + react-redux + redux .
image

Kode terlihat seperti ini:

import React, { Suspense } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Redirect } from 'react-router-dom'
import PropTypes from 'prop-types'
import { userActions, cabinetActions, tokensActions } from '../../actions'
import { CircularProgress } from '@material-ui/core'
import { api } from './api'

const Cabinet = ({ resource }) => {
    resource.read()
    return <h1>cabinet</h1>
}

Cabinet.propTypes = {
    resource: PropTypes.shape({
        read: PropTypes.func
    })
}

const CabinetPage = ({
    failedToLoad,
    tokensRefreshFailed,
    logout,
    loadCabinet,
    clearTokens,
    clearCabinet
}) => {
    if (tokensRefreshFailed || failedToLoad) {
        clearTokens()
        clearCabinet()
        logout()
        return <Redirect to='/login' />
    }

    return (
        <Suspense fallback={<CircularProgress />}>
            <Cabinet resource={loadCabinet()} />
        </Suspense>
    )
}

CabinetPage.propTypes = {
    loadCabinet: PropTypes.func,
    failedToLoad: PropTypes.bool,
    tokensRefreshFailed: PropTypes.bool,
    logout: PropTypes.func,
    clearTokens: PropTypes.func,
    clearCabinet: PropTypes.func
}

const mapStateToProps = ({
    alert,
    tokens: { tokensRefreshFailed },
    cabinet: { failedToLoad }
}) => ({
    alert,
    tokensRefreshFailed,
    failedToLoad
})

const mapDispatchToProps = dispatch =>
    bindActionCreators(
        {
            cabinetLoad: cabinetActions.load,
            logout: userActions.logoutWithoutRedirect,
            loadCabinet: api.loadCabinet,
            clearCabinet: cabinetActions.clear,
            clearTokens: tokensActions.clear
        },
        dispatch
    )

export default connect(mapStateToProps, mapDispatchToProps)(CabinetPage)

loadCabinet() mengirimkan tindakan asinkron dari rendering tiga fase seperti yang dikatakan oleh dokumen bersamaan, dan mengembalikan objek dengan prop read() .
Namun, saya tidak dapat melihat pembaruan orang tua di sini.

@ h0tw4t3r Anda mengirimkan tindakan Redux selama render komponen. Ini tidak didukung, dan tentang itulah peringatannya. Sebaiknya tanyakan kepada ahli React Router tentang cara menangani kasus ini (redirect) dengan anggun, tidak dapat membantu dengan bagian itu.

Mengenai mode bersamaan, harap dicatat Redux saat ini tidak kompatibel dengannya secara umum. Jadi Anda mungkin ingin menghindarinya jika Anda bereksperimen dengan CM.

Pembaruan kecil di utas ini

Kami akan segera merilis patch React yang memperbaiki over-firing peringatan ini untuk kelas. Jadi jika Anda mengalami ini, pertimbangkan untuk menunggu beberapa hari dan kemudian mencoba 16.13.1 ketika sudah keluar.

Jika Anda ingin terus mencari penyebabnya, saya harap https://github.com/facebook/react/issues/18178#issuecomment -595846312 menjelaskan caranya.

Saya merasa sangat aneh bahwa logika yang sama ketika digunakan dalam komponen kelas tidak memberikan peringatan apa pun sementara fungsional (kait) tidak:

Komponen fungsional (Kait):

import React, { Component } from "react"
import SortableTree from "react-sortable-tree"
import "react-sortable-tree/style.css"

const data = [
  {
    title: "Windows 10",
    subtitle: "running",
    children: [
      {
        title: "Ubuntu 12",
        subtitle: "halted",
        children: [
          {
            title: "Debian",
            subtitle: "gone"
          }
        ]
      },
      {
        title: "Centos 8",
        subtitle: "hardening"
      },
      {
        title: "Suse",
        subtitle: "license"
      }
    ]
  }
]

const nodeInfo = row => console.log(row)

export default class App extends Component {
  constructor(props) {
    super(props)

    this.state = {
      searchString: "",
      searchFocusIndex: 0,
      searchFoundCount: null,
      treeData: data
    }
  }

  render() {
    const { searchString, searchFocusIndex, searchFoundCount } = this.state

    const customSearchMethod = ({ node, searchQuery }) =>
      searchQuery &&
      ((node.title &&
        node.title.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1) ||
        (node.subtitle &&
          node.subtitle.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1))

    const selectPrevMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFoundCount + searchFocusIndex - 1) % searchFoundCount
            : searchFoundCount - 1
      })

    const selectNextMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFocusIndex + 1) % searchFoundCount
            : 0
      })

    return (
      <div>
        <h2>Find the needle!</h2>
        <form
          style={{ display: "inline-block" }}
          onSubmit={event => {
            event.preventDefault()
          }}
        >
          <input
            id="find-box"
            type="text"
            placeholder="Search..."
            style={{ fontSize: "1rem" }}
            value={searchString}
            onChange={event =>
              this.setState({ searchString: event.target.value })
            }
          />
          &nbsp;
          <button
            type="button"
            disabled={!searchFoundCount}
            onClick={selectPrevMatch}
          >
            &lt;
          </button>
          &nbsp;
          <button
            type="submit"
            disabled={!searchFoundCount}
            onClick={selectNextMatch}
          >
            &gt;
          </button>
          &nbsp;
          <span>
            &nbsp;
            {searchFoundCount > 0 ? searchFocusIndex + 1 : 0}
            &nbsp;/&nbsp;
            {searchFoundCount || 0}
          </span>
        </form>

        <div style={{ height: 300 }}>
          <SortableTree
            treeData={this.state.treeData}
            onChange={treeData => this.setState({ treeData })}
            searchMethod={customSearchMethod}
            searchQuery={searchString}
            searchFocusOffset={searchFocusIndex}
            searchFinishCallback={matches =>
              this.setState({
                searchFoundCount: matches.length,
                searchFocusIndex:
                  matches.length > 0 ? searchFocusIndex % matches.length : 0
              })
            }
            generateNodeProps={row => {
              return {
                title: row.node.title,
                subtitle: (
                  <div style={{ lineHeight: "2em" }}>{row.node.subtitle}</div>
                ),
                buttons: [
                  <button
                    type="button"
                    className="btn btn-outline-success"
                    style={{
                      verticalAlign: "middle"
                    }}
                    onClick={() => nodeInfo(row)}
                  >
                    ℹ
                  </button>
                ]
              }
            }}
          />
        </div>
      </div>
    )
  }
}

image

Komponen kelas:

import React from "react";
import SortableTree from "react-sortable-tree";
import "react-sortable-tree/style.css";

const data = [
  {
    title: "Windows 10",
    subtitle: "running",
    children: [
      {
        title: "Ubuntu 12",
        subtitle: "halted",
        children: [
          {
            title: "Debian",
            subtitle: "gone"
          }
        ]
      },
      {
        title: "Centos 8",
        subtitle: "hardening"
      },
      {
        title: "Suse",
        subtitle: "license"
      }
    ]
  }
];

const nodeInfo = row => console.log(row);

export default class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchString: "",
      searchFocusIndex: 0,
      searchFoundCount: null,
      treeData: data
    };
  }

  render() {
    const { searchString, searchFocusIndex, searchFoundCount } = this.state;

    const customSearchMethod = ({ node, searchQuery }) =>
      searchQuery &&
      ((node.title &&
        node.title.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1) ||
        (node.subtitle &&
          node.subtitle.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1));

    const selectPrevMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFoundCount + searchFocusIndex - 1) % searchFoundCount
            : searchFoundCount - 1
      });

    const selectNextMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFocusIndex + 1) % searchFoundCount
            : 0
      });

    return (
      <div>
        <h2>Find the needle!</h2>
        <form
          style={{ display: "inline-block" }}
          onSubmit={event => {
            event.preventDefault();
          }}
        >
          <input
            id="find-box"
            type="text"
            placeholder="Search..."
            style={{ fontSize: "1rem" }}
            value={searchString}
            onChange={event =>
              this.setState({ searchString: event.target.value })
            }
          />
          &nbsp;
          <button
            type="button"
            disabled={!searchFoundCount}
            onClick={selectPrevMatch}
          >
            &lt;
          </button>
          &nbsp;
          <button
            type="submit"
            disabled={!searchFoundCount}
            onClick={selectNextMatch}
          >
            &gt;
          </button>
          &nbsp;
          <span>
            &nbsp;
            {searchFoundCount > 0 ? searchFocusIndex + 1 : 0}
            &nbsp;/&nbsp;
            {searchFoundCount || 0}
          </span>
        </form>

        <div style={{ height: 300 }}>
          <SortableTree
            treeData={this.state.treeData}
            onChange={treeData => this.setState({ treeData })}
            searchMethod={customSearchMethod}
            searchQuery={searchString}
            searchFocusOffset={searchFocusIndex}
            searchFinishCallback={matches =>
              this.setState({
                searchFoundCount: matches.length,
                searchFocusIndex:
                  matches.length > 0 ? searchFocusIndex % matches.length : 0
              })
            }
            generateNodeProps={row => {
              return {
                title: row.node.title,
                subtitle: (
                  <div style={{ lineHeight: "2em" }}>{row.node.subtitle}</div>
                ),
                buttons: [
                  <button
                    type="button"
                    className="btn btn-outline-success"
                    style={{
                      verticalAlign: "middle"
                    }}
                    onClick={() => nodeInfo(row)}
                  >
                    ℹ
                  </button>
                ]
              };
            }}
          />
        </div>
      </div>
    );
  }
}

@radulle Ini bukan contoh yang sangat membantu dengan sendirinya. Saya mencoba memasukkannya ke CodeSandbox tetapi tidak berhasil: https://codesandbox.io/s/clever-taussig-9xixs . Bisakah Anda menyiapkan contoh yang bisa kami coba?

@gaearon Saya ingin membuat kode dan kotak tetapi versi terbaru perpustakaan memiliki beberapa masalah dengannya. Kesalahan tidak dilemparkan ke versi lama. Saat ini tampaknya satu-satunya cara untuk mereproduksinya adalah dengan memutarnya di Create React App secara lokal :(

@radulle Versi apa yang bisa saya coba yang akan berfungsi di CodeSandbox?

@gaearon 2.6.2 berfungsi dan
image
Jadi untuk pengaturan yang sama:
Komponen fungsional: kesalahan/peringatan
Komponen kelas: tidak ada kesalahan/peringatan
Mungkin saya melewatkan sesuatu dan mereka tidak setara.

Ya, ini adalah salah satu kasus yang saya rujuk di https://github.com/facebook/react/issues/18178#issuecomment -600369392. Kami akan menonaktifkan peringatan dalam kasus ini. Peringatan itu sendiri sah dan seperti yang Anda katakan dengan benar, secara konseptual itu juga merupakan masalah di kelas. Namun, perbedaannya tidak masuk akal sehingga kami akan membisukannya untuk kedua kasus untuk saat ini ketika itu berasal dari kelas (yang, dalam contoh ini).

Saya percaya ada kasus penggunaan yang sah untuk mengaktifkan pembaruan status dari fungsi render dan bukan dari efek, dan itu untuk mempertahankan urutan eksekusi.

Sebagai ilustrasi dengan contoh praktis: di aplikasi kami, kami memiliki sistem khusus untuk mengelola remah roti.

  • Kami memiliki konteks global yang menampung semua remah roti
  • Kami memiliki pengait yang memungkinkan kami menambahkan remah roti ke konteks ini, seperti ini:

    const addBreadcrumb = useAddBreadcrumb();
    addBreadcrumb(<Breadcrumb>{item.name}</Breadcrumb>, [item.name]);
    
  • Kami memiliki komponen yang membaca informasi dari konteks, dan merender semua remah roti. Jadi, kapan pun kita ingin merender remah roti, kita cukup memanggilnya tanpa params.

Ini sangat praktis, karena kita tidak perlu memelihara struktur remah roti: struktur ini dideklarasikan dalam kode itu sendiri. Jika kita ingin memindahkan rute ke bagian lain dari aplikasi, sistem breadcrumb akan tetap bekerja.

Jadi, dikombinasikan dengan react-router , kita dapat melakukan sesuatu seperti ini:

// Main/index.tsx
import { useAddBreadcrumb } from 'components/Breadcrumbs';
import React from 'react';
import Home from './Home';
import Movies from './Movies';

const Main = () => {
    const addBreadcrumb = useAddBreadcrumb();
    addBreadcrumb(<Breadcrumb to="/">Home</Breadcrumb>, []);

    return <Switch>
        <Route path="/movies">
            <Movies />
        </Route>
        <Route path="/" />
            <Home />
        </Route>
    </Switch>
}

// Movies/index.tsx
import { useAddBreadcrumb } from 'components/Breadcrumbs';
import React from 'react';
import Detail from './Detail';
import Master from './Master';

const Movies = ({ url }) => {
    const addBreadcrumb = useAddBreadcrumb();
    addBreadcrumb(<Breadcrumb to={url}>Movies</Breadcrumb>, [url]);

    return <Switch>
        <Route path="/:id">
            <Detail />
        </Route>
        <Route path="/" />
            <Master />
        </Route>
    </Switch>
}

// Movies/Detail/index.tsx
import Breadcrumbs, { useAddBreadcrumb } from 'components/Breadcrumbs';
import React from 'react';
import { useRouteMatch } from 'react-router-dom';

const MovieDetail = ({ url }) => {
    const addBreadcrumb = useAddBreadcrumb();
    const { params: { id } } = useRouteMatch<{ id: string; }>();
    const movie = useMovie(id);

    addBreadcrumb(
        <Breadcrumb to={url}>{movie?.name}</Breadcrumb>,
        [movie?.name, url]
    );

    return <div>
        <Breadcrumbs />
    </div>
}

Sekarang, jika kita pergi ke /movies/gone-with-the-wind , remah roti kita akan terlihat seperti:

Home > Movies > Gone with the wind

Sekarang, inilah poin saya: agar itu berfungsi, kita membutuhkan perintah eksekusi untuk dijamin. Dalam hal ini, urutan eksekusinya jelas: pertama Main merender, lalu merender turunannya, yang mencakup Movies , dan terakhir MovieDetail . Dalam hal ini, panggilan addBreadcrumb akan dieksekusi dalam urutan yang benar.

Sekarang, changelog menyatakan ini:

Dalam kasus yang jarang terjadi di mana Anda dengan sengaja ingin mengubah status komponen lain sebagai hasil dari rendering, Anda dapat menggabungkan panggilan setState ke dalam useEffect.

Ini memang salah satu kasus langka di mana kita dengan sengaja ingin mengubah status komponen lain. Namun, jika kita melakukannya, changelog menyarankan dan membungkus addBreadcrumb (yang pada akhirnya adalah setState ) yang dimuliakan menjadi useEffect , urutan eksekusi tidak dijamin lagi. Ketiga setStates akan dieksekusi setelah rendering selesai, yang menciptakan kondisi balapan, dan merusak sistem kami.

Saya tidak tahu apakah sistem aneh ini dianggap sebagai anti-pola, tetapi bagi saya itu masuk akal, dan saya belum menemukan alternatif yang lebih sederhana.

Jadi, sebagai kesimpulan, saya menyambut baik peringatan baru ini, tetapi saya pikir solusi optimal bagi kita adalah memiliki cara untuk menekannya. Mungkin parameter opsional kedua ke setState akan berhasil.

@MeLlamoPablo

Mengandalkan panggilan render yang berada di urutan saudara kandung atau perintah render orang tua/anak terdengar sangat rapuh. React sebenarnya tidak menjamin hal ini. Anak-anak dapat (dan akan) membuat ulang tanpa orang tua mereka, begitu juga sebaliknya. Jika anak-anak diberikan dengan penundaan (misalnya pemecahan kode) kode ini juga akan rusak. Atau jika beberapa anak dimasukkan atau dihapus secara dinamis. Belum lagi, ini adalah kinerja yang sangat buruk karena Anda memiliki begitu banyak cascading renders.

Saya berempati dengan masalah yang Anda coba selesaikan — memang tidak ada solusi idiomatis di React hari ini. Kami memiliki beberapa ide tentang itu tetapi solusi yang tepat harus sangat berbeda. Sementara itu, kami sangat tidak menyarankan solusi ini.

@gaearon , terima kasih atas wawasan Anda. Saya punya satu pertanyaan: apakah urutan render pertama dijamin? Karena hanya itu yang kita butuhkan untuk berfungsi dengan baik (setelah kita mengetahui urutan remah roti, kita tidak peduli dengan urutan render berikutnya).

Tampaknya logis bagi saya bahwa urutan render pertama dijamin. Bagaimana React mengetahui bahwa komponen induk memiliki anak jika tidak?

Tentang kinerja render cascading, Anda benar sekali. Saya akan mencari cara untuk meningkatkan sistem kami.

@MeLlamoPablo

Saya punya satu pertanyaan: apakah urutan render pertama dijamin? Karena hanya itu yang kita butuhkan untuk berfungsi dengan baik (setelah kita mengetahui urutan remah roti, kita tidak peduli dengan urutan render berikutnya).

Tidak kuat. Saya pikir itu sebagian besar bekerja di versi React saat ini, tetapi itu mungkin berubah di masa depan. Bahkan saat ini, digabungkan dengan fitur seperti lazy dan Suspense tidak dijamin.

Bagaimana React mengetahui bahwa komponen induk memiliki anak jika tidak?

Pesanan saudara tidak dijamin. Untuk pesanan orang tua/anak, Anda orang tua yang benar harus merender terlebih dahulu; namun, React mungkin perlu merender ulang induk sebelum sampai ke anak, atau bahkan setelah merender anak pertama tetapi sebelum yang kedua. Sekali lagi, setelah Anda menambahkan fitur seperti pemecahan kode, Anda kehilangan jaminan lebih cepat.

Ini rapuh.

@gaearon , sekali lagi terima kasih. Ini sangat dihargai.

Mungkin harus ada aturan ESLint yang memperingatkan agar tidak memanggil mutator useState dalam tubuh render?

Memanggil setState dari komponen Anda sendiri selama render adalah pola yang didukung (meskipun harus digunakan dengan hemat). Ini setState pada komponen lain yang buruk. Anda tidak dapat mendeteksinya secara statis.

Secara teoritis dapat dilakukan menggunakan ts-eslint, dengan asumsi mereka menggunakan tipe React upstream yang benar, tapi ya, mungkin lebih banyak usaha daripada nilainya.

Saya tidak berpikir itu bisa dilakukan tanpa semacam pelacakan efek. Segera setelah Anda memiliki satu fungsi di tengah, Anda kehilangan informasi.

Saya juga menghadapi masalah ini dengan react@experimental + react-redux + redux .
image

Kode terlihat seperti ini:

import React, { Suspense } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Redirect } from 'react-router-dom'
import PropTypes from 'prop-types'
import { userActions, cabinetActions, tokensActions } from '../../actions'
import { CircularProgress } from '@material-ui/core'
import { api } from './api'

const Cabinet = ({ resource }) => {
  resource.read()
  return <h1>cabinet</h1>
}

Cabinet.propTypes = {
  resource: PropTypes.shape({
      read: PropTypes.func
  })
}

const CabinetPage = ({
  failedToLoad,
  tokensRefreshFailed,
  logout,
  loadCabinet,
  clearTokens,
  clearCabinet
}) => {
  if (tokensRefreshFailed || failedToLoad) {
      clearTokens()
      clearCabinet()
      logout()
      return <Redirect to='/login' />
  }

  return (
      <Suspense fallback={<CircularProgress />}>
          <Cabinet resource={loadCabinet()} />
      </Suspense>
  )
}

CabinetPage.propTypes = {
  loadCabinet: PropTypes.func,
  failedToLoad: PropTypes.bool,
  tokensRefreshFailed: PropTypes.bool,
  logout: PropTypes.func,
  clearTokens: PropTypes.func,
  clearCabinet: PropTypes.func
}

const mapStateToProps = ({
  alert,
  tokens: { tokensRefreshFailed },
  cabinet: { failedToLoad }
}) => ({
  alert,
  tokensRefreshFailed,
  failedToLoad
})

const mapDispatchToProps = dispatch =>
  bindActionCreators(
      {
          cabinetLoad: cabinetActions.load,
          logout: userActions.logoutWithoutRedirect,
          loadCabinet: api.loadCabinet,
          clearCabinet: cabinetActions.clear,
          clearTokens: tokensActions.clear
      },
      dispatch
  )

export default connect(mapStateToProps, mapDispatchToProps)(CabinetPage)

loadCabinet() mengirimkan tindakan asinkron dari rendering tiga fase seperti yang dikatakan oleh dokumen bersamaan, dan mengembalikan objek dengan prop read() .
Namun, saya tidak dapat melihat pembaruan orang tua di sini.

Bagi siapa pun yang mengalami masalah ini, saya memperbaikinya dengan memindahkan pengiriman tindakan redux ke komponen yang dikembalikan. Begini tampilannya sekarang:

const CabinetPage = ({
    alert,
    failedToLoad,
    tokensRefreshFailed,
    logout,
    loadCabinet,
    clearTokens,
    clearCabinet,
    clearAlert
}) => (
    <Suspense fallback={<MUIBackdropProgress />}>
        {alert.message && (failedToLoad || tokensRefreshFailed) ? (
            <MUIAlertDialog
                title={alert.message}
                text={errorText}
                onClose={() => {
                    clearAlert()
                    clearCabinet()
                    clearTokens()
                    logout()
                }}
            />
        ) : (
            <Cabinet resource={loadCabinet()} />
        )}
    </Suspense>
)

Saya suka peringatan ini karena memaksa Anda untuk memilih pola desain yang tepat. Sekarang semuanya bekerja dengan sempurna!

Kami memperbaiki kasus-kasus di mana peringatan itu menyala berlebihan di 16.13.1. Kasus-kasus yang tersisa sah dan perlu diperbaiki.

@gaearon baru saja ditingkatkan dan kesalahannya hilang! Terima kasih teman-teman atas pekerjaan Anda!

@gaearon terima kasih. Anda baru saja menyelamatkan hari saya :-)

Meskipun memutakhirkan tidak menyelesaikan masalah saya, itu memberi saya lebih banyak informasi di konsol untuk membantu menemukan masalah saya. Terima kasih @gaearon !

Bagaimana jika Anda mengirimkan tindakan yang menyebabkan komponen lain mengembalikan status yang sama seperti terakhir kali? Apakah itu dianggap buruk? Saya akan berpikir itu akan korsleting dalam kasus itu.

Bisakah saya mengatakan bahwa ini sementara saya benar-benar memahami logika di balik peringatan ini ... hampir terasa seperti pengkhianatan dari apa yang tim React katakan kepada komunitas, karena rasanya tim telah mengajarkan kebenaran penting tentang cara membuat kode Reaksi:

1) pertahankan status Anda setinggi yang Anda butuhkan dalam hierarki Anda (tidak lebih tinggi), lalu berikan data dan penyetel ke komponen anak

2) Komponen fungsi luar biasa! Lupakan kebisingan kelas itu, lakukan seluruh komponen Anda dalam satu fungsi!

Dan sekarang ketika orang mengikuti dua aturan itu, dan menurunkan state setter mereka ke komponen fungsinya, dan memanggil mereka di komponen itu ... Anda menarik karpet keluar dan pergi "ha, tapi Anda tidak bisa melakukan apa yang kami katakan melakukan".

Semua ini tidak mengubah apa pun tentang kebutuhan teknis di sini, dan saya tidak mengatakan apa pun tentang perubahan ini salah atau buruk ... Saya hanya merasa ada masalah pengiriman pesan, karena Anda tidak mengomunikasikan aturan yang jelas dan baik (seperti keduanya Saya baru saja menyebutkan) untuk pengkodean di dunia baru ini. Jika Anda akan mengubah aturan pada kami, saya pikir akan sangat membantu untuk melakukannya dengan terlebih dahulu menjelaskan praktik terbaik.

Jadi, yang benar-benar saya tanyakan adalah ... Saya pikir akan lebih ideal jika seseorang dalam tim menulis sesuatu (seperti artikel) di mana mereka berkata, "Saya tahu kami memberi Anda dua aturan itu sebelumnya: ini dia aturan baru", dan kemudian tambahkan tautan ke artikel apa itu di setiap tempat di dokumen/catatan rilis yang merujuk peringatan baru ini (jadi semua orang yang mencari di googling "WTF apakah ini?" dapat mempelajari cara yang tepat untuk membuat kode Bereaksi, di "baru dunia").

@machineghost : Saya pikir Anda salah paham tentang pesan yang diperingatkan.

Tidak ada yang salah dengan meneruskan panggilan balik ke anak-anak yang memperbarui status pada orang tua. Itu selalu baik-baik saja.

Masalahnya adalah ketika satu komponen mengantri pembaruan di komponen lain, _sementara komponen pertama sedang rendering_.

Dengan kata lain, jangan lakukan ini:

function SomeChildComponent(props) {
    props.updateSomething();
    return <div />
}

Tapi ini baik-baik saja:

function SomeChildComponent(props) {
    // or make a callback click handler and call it in there
    return <button onClick={props.updateSomething}>Click Me</button>
}

Dan, seperti yang telah ditunjukkan Dan beberapa kali, mengantri pembaruan di komponen _same_ saat rendering juga baik-baik saja:

function SomeChildComponent(props) {
  const [number, setNumber] = useState(0);

  if(props.someValue > 10 && number < 5) {
    // queue an update while rendering, equivalent to getDerivedStateFromProps
    setNumber(42);
  }

  return <div>{number}</div>
}

Benar, tetapi ketika Anda mengkodekan komponen Anda, Anda tidak memikirkan waktu induknya. Itulah bagian dari keindahan komponen React: enkapsulasi.

Jadi sekali lagi, saya tidak mengatakan peringatan baru itu buruk sama sekali, saya katakan sebelumnya kami memiliki dua aturan yang dapat diikuti oleh pengembang React yang baik. Sekarang di bawah kondisi X aturan-aturan itu keluar dari jendela, tetapi hanya di bawah X (di mana terdengar seperti X = "sementara komponen induk juga memperbarui").

Saya hanya berpikir perlu ada lebih banyak fokus untuk menjelaskan itu, dan tentang bagaimana menghindari masalah, daripada hanya menjadi seperti "ini adalah masalah sekarang!".

@machineghost : Anda _truly_ tidak mengerti apa yang saya katakan di sini.

Waktu orang tua/anak bukanlah masalah.

Status antrian memperbarui _dalam komponen lain saat merender fungsi komponen_ adalah masalahnya.

Menurut definisi itu harus menjadi orang tua/anak (atau cucu): bagaimana lagi itu bisa menurunkan setter negara?

Saya tidak mengatakan ini juga tidak bisa menjadi masalah dalam hubungan komponen lain, tetapi saya secara khusus berbicara tentang orang-orang yang mengikuti praktik terbaik React, menurunkan state setter, dan kemudian mendapatkan peringatan ini.

Itu saja yang saya bicarakan, dan yang saya katakan adalah, "ini bisa dijelaskan dengan lebih baik, dengan lebih fokus pada cara membuat kode dengan baik daripada hanya 'ini kesalahan baru dan inilah artinya'".

Waktu. Bukan. Isu.

Komponen fungsi diizinkan untuk mengantri pembaruan status, saat merender, hanya untuk dirinya sendiri . Seperti yang ditunjukkan oleh contoh saya, yang bertindak setara dengan getDerivedStateFromProps .

Mengantri pembaruan untuk _any_ komponen lain dari dalam badan rendering sebenarnya dari komponen fungsi adalah ilegal.

Itulah yang dikatakan peringatan ini kepada Anda.

Saya tidak tahu bagaimana saya bisa menjelaskannya dengan lebih jelas.

Waktu bukanlah masalahnya: bukan milikmu, bukan milikku. Masalah saya adalah dokumentasi, atau kekurangannya.

Tapi Anda telah memutuskan untuk memulai perang di utas masalah dengan orang asing di Internet, alih-alih mendengarkan apa yang mereka katakan dan ... Saya tidak punya keinginan untuk terus terlibat dengan Anda.

Intinya tidak ada aturan yang berubah. Ini selalu menjadi pola yang salah. Itu baru saja disorot sehingga Anda sadar bahwa kode Anda bermasalah.

Dan secara harfiah tidak ada yang baru saja Anda katakan tidak setuju dengan apa pun yang saya tulis. Bahkan, hampir seperti saya telah mengatakan hal yang sama persis dari awal ... dan semua yang saya minta selama ini adalah penjelasan yang lebih baik dari aturan yang sama, yang Anda katakan tidak berubah (dan tentu saja mereka belum ... apa yang berubah dan "membuat dunia baru" adalah peringatannya).

PS Anda juga sepertinya gagal menyadari ironi di sini. Jika saya gagal memahami apa pun, itu berarti dokumentasi dapat ditingkatkan. Meneriaki saya tentang betapa buruknya saya memahami hal-hal hanya memperkuat posisi saya; itu tidak secara ajaib meningkatkan dokumentasi.

Halo teman-teman, mari kita sedikit tenang. 🙂

@markerikson Menghargai Anda ikut serta, tetapi saya pikir diskusi ini agak terlalu panas.

@machineghost Terima kasih telah mengungkapkan kekhawatiran Anda. Saya tahu itu menjengkelkan ketika peringatan baru muncul untuk pola yang mungkin tampak tidak berbahaya sebelumnya.

Saya setuju peringatan ini memerlukan beberapa konteks sebelumnya. Pada dasarnya, Anda perlu mengetahui dua hal dari era kelas:

  • Bahwa Anda tidak boleh mengatur Status selama render. Kelas selalu memperingatkan tentang ini.

  • Tubuh komponen fungsi itu pada dasarnya sama dengan metode render komponen kelas.

Memang kelalaian kami bahwa setState pada komponen lain selama badan komponen fungsi tidak memperingatkan sebelumnya. Anda dapat menyimpulkan bahwa itu adalah pola yang buruk dari dua poin di atas, tetapi cukup adil untuk mengatakan bahwa seseorang tidak dapat menyadarinya. Kami mohon maaf atas ketidaknyamanannya.

Jika Anda merasa ada tempat tertentu dalam dokumen di mana hal ini harus disebutkan, silakan ajukan masalah pada repositori dokumen. Kami merencanakan penulisan ulang dokumen yang akan berbasis di sekitar Hooks sehingga itu adalah sesuatu yang dapat kami ingat. Terima kasih!

Saya sama sekali tidak bermaksud membuat siapa pun merasa buruk tentang apa pun, dan saya menolak untuk menerima permintaan maaf Anda ;) Hooks adalah jenius, dan Anda semua jenius karena menciptakannya. Dan setiap insinyur yang menyalahkan insinyur lain karena tidak membayangkan dengan sempurna setiap hasil adalah ... brengsek.

Yang saya coba komunikasikan adalah, saat ini ketika saya mendapat peringatan ini, saya melakukan apa yang dilakukan semua orang: Saya mencarinya di Google. Saya kemudian menemukan halaman yang mengatakan "kami mendapat peringatan baru ini".

Saya hanya berpikir akan lebih baik (dan masih bisa lebih baik) jika ada (bisa jadi) tautan dalam pengumuman itu, atau tempat serupa yang mungkin ditemukan seseorang dengan googling, ke "diskusi tingkat tinggi" dari "ini mengapa kami memperkenalkan kesalahan ini dan inilah cara Anda bisa menjadi pengembang React yang luar biasa dan ikuti beberapa panduan dasar untuk tidak pernah mengalaminya sendiri."

Tetapi sekali lagi, kaitnya luar biasa, tim Reactnya luar biasa, dan bahkan peringatan baru ini juga luar biasa (saya yakin ini akan mengalahkan penemuan kesalahan yang coba dicegah). Jika ada yang berutang permintaan maaf kepada siapa pun, itu saya karena tidak memimpin dengan itu.

Tentu, tidak ada perasaan sulit. Jawaban untuk "mengapa" tidak lebih kompleks daripada "jika satu komponen memicu pembaruan pada komponen lain selama rendering, menjadi sangat sulit untuk melacak aliran data aplikasi Anda karena tidak lagi berjalan dari atas ke bawah". Jadi jika Anda melakukannya, Anda membuang manfaat dari React.

Sekali lagi, untuk memperjelas, ini bukan hal baru — kelas selalu memiliki peringatan yang sama. Kami melewatkannya dengan Hooks, dan memperbaiki kesalahannya. Saya kira cara Anda memperbaikinya tergantung pada kasusnya — jadi kami tidak dapat memberikan instruksi yang tepat — tetapi dengan senang hati membantu jika Anda membagikan contoh yang Anda hadapi. Secara umum, Anda akan memperbaikinya dengan cara yang mirip dengan cara Anda memperbaiki peringatan kelas terkait yang selalu ada.

Semoga itu bisa membantu sedikit!

Saya akan menambahkan dua sen saya ke diskusi ini dan setuju dengan @machineghost bahwa ada banyak kebingungan sejak pengenalan komponen fungsional dan kait. Komunitas mencari tim reaksi untuk kepemimpinan tetapi hal-hal yang dulunya sederhana menjadi berbelit-belit dan tampaknya ada kurangnya komunikasi dan contoh yang jelas.

Kasus dan poinnya adalah ComponentDidMount dan Unmount, pertama-tama kita diberitahu untuk menggunakan komponen fungsi, kemudian menggunakan useEffect dengan array kosong, kemudian kita diberitahu bahwa itu tidak baik, sekarang kita mendapatkan pesan kesalahan ini berantakan. Saya tidak tahu, saya menghargai semua kerja keras yang dilakukan untuk bereaksi tetapi kami benar-benar membutuhkan lebih banyak upaya untuk dokumentasi dan praktik terbaik.

Saya sudah berada di kereta musik fungsi begitu lama (mencoba menghindari kelas dengan Recompose dan semacamnya bahkan sebelum kait) sehingga saya bahkan tidak ingat peringatan kelas itu.

Dan sementara saya menghargai tanggapan Anda, saya terutama hanya berharap untuk "aturan praktis", pedoman, praktik terbaik, dll. Hal-hal seperti "pertahankan status Anda setinggi yang diperlukan untuk mencakup semua komponen yang menggunakannya, tetapi tidak lebih tinggi " atau "teruskan penyetel status ke komponen turunan menggunakan inversi pola kontrol".

Mungkin tidak ada di sini, tetapi mungkin sesuatu seperti "jika komponen fungsional A berubah status, seharusnya tidak membuat komponen anak B yang melewati penyetel status (harus kembali saat itu atau sesuatu), karena jika komponen anak merender dan mengubah status Anda akan mengalami masalah".

Atau mungkin hari Minggu larut malam, saya telah bekerja sepanjang hari pada proyek pribadi, dan otak saya terlalu digoreng dan membuat sesuatu yang sederhana menjadi sesuatu yang sulit. Either way, saya akan memposting kembali jika saya memiliki saran lebih lanjut untuk pedoman, tetapi jika tidak, saya tentu tidak ingin orang lain menghabiskan Minggu malam mereka untuk ini.

Terima kasih sekali lagi!

Saya pikir kita sampai pada titik di mana utas ini telah melampaui kegunaannya.

Kasus dan poinnya adalah ComponentDidMount dan Unmount, pertama-tama kita diberitahu untuk menggunakan komponen fungsi, kemudian menggunakan useEffect dengan array kosong, kemudian kita diberitahu bahwa itu tidak baik, sekarang kita mendapatkan pesan kesalahan ini berantakan.

Maaf bahwa dokumentasi kami tidak membantu Anda, tetapi sangat sulit untuk mengatakan masalah spesifik apa yang Anda hadapi. Sayangnya ini sangat kabur dan merupakan distorsi dari apa yang kami coba katakan. Seperti permainan telepon rusak. Jika Anda memiliki masalah khusus, harap ajukan masalah baru dan coba jelaskan lebih detail. Kami akan mencoba membantu Anda jika Anda bisa lebih spesifik.

Dan sementara saya menghargai tanggapan Anda, saya hanya berharap untuk "aturan praktis", pedoman, praktik terbaik, dll

Aturan praktisnya selalu "jangan lakukan efek samping saat rendering". Pikirkan rendering sebagai perhitungan murni. Efek samping masuk ke tempat yang berbeda (metode siklus hidup di kelas, atau useEffect di komponen fungsi). Tidak ada banyak lagi untuk itu.

"jika komponen fungsional A berubah status, itu seharusnya tidak membuat komponen anak B yang melewati penyetel status (harus kembali saat itu atau sesuatu), karena jika komponen anak merender dan mengubah status, Anda akan memiliki masalah".

Saya pikir masih ada beberapa kesalahpahaman di sini. Tidak apa-apa untuk meneruskan setter status ke komponen anak. Selalu baik-baik saja, dan akan selalu baik-baik saja.

Masalahnya adalah memanggil mereka saat merender . Itu umumnya harus benar-benar tidak perlu. Sulit bagi saya untuk menebak mengapa Anda melakukannya tanpa contoh nyata. Jadi sulit untuk membantu.

Tema umum dalam kedua percakapan ini adalah bahwa kita berputar-putar. Diskusi telah beralih menjadi meta, dan alih-alih berbicara tentang kasus-kasus tertentu, kami membahas hal-hal umum yang tidak jelas. Kemungkinan kita salah paham satu sama lain, tetapi kurangnya contoh nyata membuat kesalahpahaman ini tidak mungkin diselesaikan.

Untuk alasan ini saya akan mengunci utas ini. Saya sangat menghargai masukan semua orang di sini, dan kami akan senang mendengar lebih banyak dari Anda jika Anda kesulitan memperbaiki peringatan ini. Cara mendapatkan bantuan adalah dengan mengajukan masalah dengan contoh reproduksi minimal. Kemudian kita bisa mendiskusikan masalah konkret Anda dan membantu brainstorming solusi. Ini akan lebih produktif untuk semua orang yang terlibat, dan juga akan menghindari pengiriman email ke lusinan orang yang telah mengomentari utas ini dan akhirnya berlangganan. Terima kasih!

Sebagai pembaruan kecil (maaf untuk ping semua orang), saya mendengar https://github.com/final-form/react-final-form/issues/751#issuecomment -606212893 menyelesaikan banyak kesalahan ini untuk orang-orang yang menggunakannya Perpustakaan.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat