Definitelytyped: React.d.ts ReadOnly<t>di negara bagian dan alat peraga</t>

Dibuat pada 25 Jan 2017  ·  91Komentar  ·  Sumber: DefinitelyTyped/DefinitelyTyped

Hai @ericanderson

Saya mengalami banyak masalah dengan perubahan ini saat digunakan dalam praktik:

Masalah 1: Pergi ke Definisi

Saat Anda menekan Go To Definition pada properti props atau state, TypeScript tidak dapat menyelesaikannya.

interface MyComponentProps {
    name: string;
}

export abstract class MyComponent extends React.Component<MyComponentProps , void> {
    myMethood() {
       this.props.name; //<-- Go To definition in name
   }
}

image

Masuk akal karena anggota dibuat secara sintetis, tetapi tetap mengganggu.

Masalah 2: Hirarki komponen (P Generik dengan kendala)

Lebih penting lagi, jika Anda membuat komponen abstrak seperti ini:

interface MyBaseProps {
    onChange?: (val: any) => void;
}

export abstract class MyBase<P extends MyBaseProps> extends React.Component<P, void> {
    myMethood() {
        this.props.onChange!(2); //The type is S["onChange"] instead of (val: any) => void and so is not invocable. 
   }
}

TS dapat menunjukkan bahwa ada properti onChange, tetapi terkadang tidak dapat menemukan tipenya.

image

Ini adalah perubahan paling penting karena jika mencegah saya untuk memiliki hierarki komponen yang memiliki properti dan fungsionalitas yang sama. Sepertinya ada masalah di kompiler TS tetapi sampai diperbaiki.

Masalah 3: Tidak begitu Hanya-baca.

Meskipun saya setuju bahwa perubahan ini menangkap dengan baik maksud fungsional dari React, ada situasi yang valid di mana Anda dapat memodifikasi status secara imperatif, seperti di konstruktor, dan juga jika Anda mengubah status dan memanggil forceUpdate semuanya berfungsi dengan baik.

C# this.state.name = "John"; this.forceUpdate(); //Ok as long as you don't setState afterwards, but calling setState also is annoying with the callback.

Apakah itu direkomendasikan? Tidak.
Apakah itu dilarang? Juga tidak, jika tidak forceUpdate tidak akan ada.

Tentu saja Anda dapat memasukkan status ke S (atau any ) dan membuat perubahan, tetapi jika itu pola yang umum menjadi rumit.

Kesimpulan: Apakah itu layak?

Saya merasa sedih bahwa fitur mengkilap baru dari TS membuat lebih banyak masalah daripada solusi dalam kasus ini, tapi jujur ​​saya pikir inilah masalahnya di sini.

Di sisi lain, perubahan setState bagus 👍 , tidak tahu tentang Pick<S,K> .

Komentar yang paling membantu

Masalah 3 untuk diperdebatkan, kurasa.

Anda benar bahwa Anda dapat _secara teknis_ melakukan contoh di atas di React, tetapi saya pasti akan berpendapat bahwa itu bukan cara React yang dimaksudkan untuk digunakan.

Ini dapat dipecah menjadi 3 kasus berbeda.

Inisialisasi Umum

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  public state: State = {
    bar: 5,
  };
}

Inisialisasi berdasarkan alat peraga

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      bar: props.baz,
    };

    // or
    this.setState({
      bar: props.baz,
    });
  }
}

Tugas acak dengan forceUpdate

Mengingat bahwa menurut saya lebih baik mendorong orang ke arah yang "benar", Anda dapat dengan mudah mengatasi masalah ini dengan mendeklarasikan ulang public state :

interface State {
  bar: number;
}

class Foo extends React.Component<{}, State> {
  public state: State;
  public myMethod() {
    this.state.bar = 5;
  }
}

Semua 91 komentar

Versi TypeScript apa yang digunakan studio visual Anda?

@vsaio untuk sa

Untuk masalah 1, dengan TS 2.1.5 dan VSCode terbaru, ini berfungsi dengan baik untuk saya. Saya tidak punya windows/VS jadi saya tidak bisa memeriksanya, tapi saya yakin ada pembaruan untuk plugin Anda atau Anda tidak menggunakan TS 2.1.5

Sama untuk Soal 2

VS 2015 dengan TS 2.1.5.0

Masalah 3 untuk diperdebatkan, kurasa.

Anda benar bahwa Anda dapat _secara teknis_ melakukan contoh di atas di React, tetapi saya pasti akan berpendapat bahwa itu bukan cara React yang dimaksudkan untuk digunakan.

Ini dapat dipecah menjadi 3 kasus berbeda.

Inisialisasi Umum

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  public state: State = {
    bar: 5,
  };
}

Inisialisasi berdasarkan alat peraga

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      bar: props.baz,
    };

    // or
    this.setState({
      bar: props.baz,
    });
  }
}

Tugas acak dengan forceUpdate

Mengingat bahwa menurut saya lebih baik mendorong orang ke arah yang "benar", Anda dapat dengan mudah mengatasi masalah ini dengan mendeklarasikan ulang public state :

interface State {
  bar: number;
}

class Foo extends React.Component<{}, State> {
  public state: State;
  public myMethod() {
    this.state.bar = 5;
  }
}

Masalah saya adalah dengan varian obat generik. khusus untuk mengetik di dalam kelas yang diketik secara umum. di bawah ini adalah contoh yang cukup minimal di mana segala sesuatunya rusak.

class TBaseState {
  public value: string;
}

function globalFunc<T extends Readonly<TBaseState>>(item: T) {
}

class MyComponent<TProps, TState extends TBaseState> extends React.Component<TProps, TState> {
  broken() {
    // typing of this.state is Readonly<TState>
    // this is not assignable to Readonly<TBase>
    globalFunc(this.state);

    // this is a horrible hack to fix the generics variance issue
    globalFunc(this.state as TState as Readonly<TBaseState>);
  }
}

class MyState extends TBaseState {
}

let component: MyComponent<any, MyState>;

// here the typing of component.state is Readonly<MyState>
// this is assignable to Readonly<TBase>
globalFunc(component.state);

Saya di TS 2.1.5.0

image

tetapi bisa jadi di VS kami memiliki pengalaman TS terburuk daripada di kode VS ...

untuk Masalah 1, buka definisi TS juga tidak berfungsi di VS Code:

interface MyComponentProps {
    name: string;
}

export abstract class MyComponent extends React.Component<MyComponentProps , void> {
    fullName: string;
    myMethood() {
       this.props.name; //<-- doesnt work
       this.fullName; //<-- works
   }
}

untuk Masalah 2 memang benar bahwa Kode VS berperilaku lebih baik:

image

sementara VS terlihat bingung:

image

Saya pikir untuk VSCode dan masalah 1, ini berfungsi karena saya menggunakan plugin untuk "Ketik TypeScript dan Tata Bahasa Javascript Terbaru" yang harus memiliki penanganan yang lebih cerdas.

@patsissons itu contoh yang menarik, meskipun saya pikir ini lebih mewakili bug di TypeScript daripada bug di file definisi. Sebagai contoh, setState digunakan untuk mengambil S yang berarti untuk melakukan sebagian kita harus melakukan trik aneh seperti setState({foo:5} as any as State) atau menggunakan yang mengambil fungsi. Saya tidak yakin kurangnya ekspresi kompiler membuat pengetikan "salah". Saya pikir ini adalah argumen yang layak untuk perubahan README untuk menandai kasus tepi ini.

Apakah Anda mengajukan masalah di TS?

Jadi perubahan ini saat ini merusak semua VS dan menonaktifkan Go To Definition di semua Kode VS kecuali jika Anda memiliki plug-in...

Juga ada argumen kelengkapan. Ada zillions API yang dimaksudkan untuk Readonly dan tidak saat ini, hanya di React.d.ts

 interface ComponentLifecycle<P, S> {
        componentWillMount?(): void;
        componentDidMount?(): void;
        componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;
        shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: Readonly<any>): boolean;
        componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: Readonly<any>): void;
        componentDidUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>, prevContext: Readonly<any>): void;
        componentWillUnmount?(): void;
    }

Saya pikir readonly harus digunakan untuk 'membekukan' atau 'Inmmutable.js' bukan untuk pemikiran panjang yang tidak dimaksudkan untuk dimodifikasi, seperti objek acara, misalnya.

Belum mengajukan, saya baru saja memperbaiki kode saya hari ini untuk menangani tipe Readonly<T> baru, ini adalah kasus yang saya temui yang saya tidak memiliki solusi yang diketik dengan benar. Silakan dan ajukan masalah, saya akan sibuk sepanjang sisa hari ini dengan kode tambalan.

Ah ya, aku tahu aku melewatkan beberapa. @olmobrutall Jika kami menyimpan perubahan status yang saya perkenalkan untuk ditandai sebagai hanya-baca, saya setuju bahwa metode tersebut harus diperbarui. Saya merasa seperti kita membutuhkan konsensus tentang hal yang benar untuk dilakukan terlebih dahulu.

Adapun jeda VS, saya tidak tahu apa yang benar. Haruskah jenisnya ditahan karena beberapa alat tidak mutakhir?

@patsissons Anda selalu dapat memberikan pengetikan Anda sendiri untuk reaksi saat ini jika Anda ingin menunggu untuk melihat bagaimana ini berjalan sebelum memperbarui semua kode Anda. https://ericlanderson.com/using-custom-typescript-definitions-with-ts-2-x-3121db84015d#.ftlkojwnb

menurut pengalaman kami, VS selalu tertinggal sedikit. Toko kami menggunakan vscode untuk melakukan pengembangan TypeScript aktif dan VS lebih banyak digunakan untuk hanya menambal file kode atau untuk pengembang non-TypeScript untuk melihat kode, tidak harus untuk pengembangan aktif.

@ericanderson peretasan tidak terlalu buruk untuk saat ini, saya hanya perlu menampi Readonly<T> untuk mendapatkan T saya yang dapat dialihkan ke Readonly<Base> .

Kita berbicara tentang 'react.d.ts', deklarasi anggota tunggal ini digunakan secara besar-besaran. Saya pikir layak untuk menahan sampai VS siap.

Juga, seperti 50% dari jenis di dunia dimaksudkan untuk dibaca saja, seperti objek yang Anda dapatkan dari API, saya rasa kita tidak perlu membubuhi keterangan itu.

Saya pikir Readonly harus digunakan untuk objek yang telah secara eksplisit dikonversi untuk memiliki properti get-only. Seperti membeku.

@olmobrutall Readonly baru, jadi praktik terbaik yang tepat tidak benar-benar ditentukan. Saya pribadi lebih suka semuanya menyatakan bahwa dibutuhkan Readonly<> hal untuk membantu menandakan bahwa itu tidak akan mengubahnya. Demikian pula, React tidak mengharapkan Anda untuk memodifikasi state di luar setState dan dengan demikian perubahan ini memastikan bahwa kecelakaan tidak menimbulkan bug, yang merupakan salah satu manfaat utama menggunakan TypeScript daripada javascript.

Jika kinerja lebih konsisten di seluruh browser untuk Object.freeze , saya akan membayangkan orang-orang Bereaksi akan benar-benar mulai membeku setelah setState .

Lalu apa tujuan dari forceUpdate?

Saya ingin tahu tentang pemikiran orang lain tentang bagaimana PastiTyped harus bekerja sehubungan dengan pembaruan perkakas serta filosofi tentang Readonly saat bereaksi (dan perpustakaan lain yang tujuannya adalah Anda tidak memodifikasi objek tertentu).

cc/ @johnnyreilly @vsaio @pspeter3 untuk pemikiran tentang reaksi secara khusus dan pemikiran lainnya secara umum
cc/ @andy-ms @mhegazy untuk pemikiran tentang bagaimana PastiTyped harus melanjutkan secara filosofis untuk pembaruan perkakas dan penggunaan Readonly yang bersemangat

@olmobrutall kami menggunakan forceUpdate untuk mengantri render di sisi reaksi, didorong dari peristiwa yang dapat diamati di sisi status.

PEMBARUAN :
Saya akan mengklarifikasi skenario kami sedikit sehingga tidak disalahpahami. Objek negara kita adalah objek abadi yang berumur panjang (jadi Readonly<T> sebenarnya sangat cocok untuk kita). Objek status ini berisi beberapa aliran rxjs yang dapat diamati yang disalurkan ke notifikasi yang dapat diamati yang disebut stateChanged . Komponen React mengamati observable ini untuk event dan menyalurkan event tersebut ke dalam panggilan ke forceUpdate (setelah debouncing). Akibatnya, negara kita yang bisa berubah hidup di dalam negara, tetapi negara itu sendiri dan anggota-anggota yang ada di negara itu semuanya tidak berubah. Ini tentu saja bukan usecase standar untuk React, tetapi alurnya sangat mirip. Kami hanya memiliki objek status pintar yang tahu cara menginformasikan komponen saat rendering ulang diperlukan.

@ericanderson masalah utamanya adalah definisi tipe ini mengalami masalah SemVer. Karena versi definisi tipe sebagian besar terkait dengan versi modul masing-masing, kami berakhir dengan versi minor bump yang membawa perubahan definisi tipe yang melanggar, yang berarti kami harus menyematkan versi @types di package.json kami mengajukan.

@olmobrutall Dari dokumen reaksi:

Biasanya Anda harus mencoba menghindari semua penggunaan forceUpdate() dan hanya membaca dari this.props dan this.state di render().

Panduan reaksi sebenarnya memberi tahu Anda untuk tidak memperbarui status secara langsung: https://facebook.github.io/react/docs/state-and-lifecycle.html#do -not-modify-state-directly

forceUpdate , seperti yang saya baca, hanya untuk memaksa komponen Anda untuk memperbarui ketika komponen Anda didasarkan pada data _not_ di props atau state Anda.

@patsissons Saya mungkin salah, tapi saya percaya SemVer dirancang agar kompatibel dengan API dan maksud semantik. Hanya karena Anda menggunakan perpustakaan dengan cara yang tidak dimaksudkan (sesuai dengan dokumen) tidak berarti bahwa perpustakaan harus tetap mendukung penggunaan yang tidak diinginkan tersebut. Penulis perpustakaan baik dalam SemVer untuk mengubah semantik yang salah tetapi kebetulan digunakan oleh beberapa orang.

Yang mengatakan, mungkin Readonly<> ke state adalah perubahan yang terlalu besar, tetapi anggaplah sejenak bahwa itu adalah perubahan yang tepat. Kapan harus dirilis ke PastiTyped? Kode Anda akan selalu perlu diubah setelah Anda mendapatkan pembaruan yang akhirnya menandai state sebagai Readonly<> .

Saya masih tidak tahu apa yang benar tentang Readonly<> diterapkan ke state yang membuatnya sulit untuk memperdebatkan semver, atau perkakas, atau apa pun. Firasat saya adalah bahwa itu benar. Orang-orang yang meninjau perubahan tidak pernah mengangkatnya sebagai masalah. Tampaknya sejalan dengan maksud dari tim React.

Saya senang untuk tunduk pada salah satu pengulas untuk bereaksi di PastiTyped (saya cc mereka semua di atas).

Jadi Observable mengubah status dan Anda forceUpdate? Jadi mengubah negara secara imperatif adalah beberapa cara yang diperbolehkan.

Saya tahu bahwa di mana Readonly harus digunakan tidak 100% ditentukan. Tetapi apakah kita perlu memulai dengan properti yang kontroversial dan digunakan secara besar-besaran sebelum alatnya siap?

Saya semua untuk tipe yang kuat, saya mengelola 6 proyek semua dengan strictNullChecks, tetapi manfaatnya di sini jauh lebih kecil daripada masalah yang dihasilkannya saat ini.

@ericanderson Saya percaya SemVer2 dirancang untuk mengizinkan deklarasi versi node seperti ^15.0.0 dan berharap bahwa setiap peningkatan minor atau patch (yaitu, 15.0.1 atau 15.1.0 ) ke modul transparan atau setidaknya kompatibel ke belakang dari perspektif eksternal. Setiap peningkatan besar ( 16.0.0 ) akan memerlukan penyesuaian pada deklarasi versi untuk membawa perubahan. Ini pada dasarnya gerbang melanggar perubahan dari dibawa ke dalam sistem pengetikan. Tetapi saat ini versi definisi tipe tidak dapat menyimpang dari versi modul masing-masing versi utama (berdasarkan konvensi), yang menghasilkan diskontinuitas ini.

Versi singkatnya adalah bahwa definisi tipe dapat memiliki perubahan yang melanggar yang diperkenalkan tanpa modul itu sendiri berubah sama sekali, dan perubahan yang melanggar memerlukan perubahan versi utama.

Tapi Anda tidak akan membuat PR menghapus forceUpdate, bukan?

Kemudian jika forceUpdate ada, mengubah status secara imperatif juga harus ada.

Secara teori Anda harus menggunakan grafik keadaan yang tidak dapat diubah, tetapi sangat sering mengubah Keadaan secara imperatif baik-baik saja dan tidak semua pengembang membeli ide struktur data yang persisten.

Untungnya React mengizinkan scape route dan membuat perubahan langsung dalam keadaan menjadi mungkin, apakah kita akan melarang pengembang TS untuk mengambil rute itu? Bukankah itu terlalu paternalistik?

Vue.js misalnya mempromosikan perubahan imperatif, saya tidak akan terkejut jika itu mempengaruhi React.

Saya juga membaca posting blog dari beberapa penulis React belum lama ini (saya ingat) mendorong menggunakan React tanpa semua upacara Redux.

Posisi saya sebaliknya adalah untuk mendapatkan definisi jenis yang diterbitkan sesegera mungkin, satu-satunya perhatian saya adalah otomatisasi. karena mengingat status definisi tipe saat ini, versi build yang berurutan tanpa perubahan sumber dapat rusak. Jenis kegagalan CI non-deterministik ini mengganggu. Tidak ada yang ingin melihat build break mereka karena mereka mendorong perubahan dan kemudian mengetahui bahwa tangan mereka tidak ada hubungannya dengan build break.

Setelah tidur di atasnya, pendapat pribadi saya adalah sebagai berikut:

  • Praktik yang baik akan melibatkan penggunaan kunci benang atau npm, jadi Anda tidak akan mendapatkan kejutan kecuali Anda memutakhirkan secara lokal terlebih dahulu.
  • Membuat status readonly adalah bagaimana React dimaksudkan untuk digunakan. Dokumentasi dan contoh mengkonfirmasi hal ini.
  • Alur kerja di mana Anda tidak ingin menggunakan setState berada dalam basis kode Anda dan dalam hak Anda sendiri. Anda benar bahwa React menyediakan forceUpdate, tetapi penggunaannya dimaksudkan untuk menyebabkan render ketika Anda berada di luar kasus penggunaan yang dimaksudkan. Jadi, jika Anda tidak ingin menggunakan status sebagaimana dimaksud, tidak apa-apa, tetapi pada saat itu Anda tidak perlu menggunakan status variabel instan. Bahkan Anda bisa menggunakan variabel pribadi biasa.
  • Ya proyek ini bergantung pada banyak orang, namun sejauh ini hanya dua keluhan yang sejauh ini membuat saya berpikir ini bukan masalah yang tersebar luas. Selanjutnya masalah yang diangkat tentang fungsi global dapat ditulis ulang untuk mengambil generik secara berbeda (lihat masalah TypeScript tertaut)

Mengingat pemikiran di atas serta solusi untuk aplikasi React non-standar, saya pikir Readonly benar dan satu-satunya perubahan yang diperlukan untuk kelengkapan adalah memperbarui metode siklus hidup agar sesuai.

Saya setuju bahwa perubahan ini benar-benar masuk akal, kasus penggunaan saya yang menyebabkan masalah adalah kasus sudut yang sangat unik yang jarang ditemui. Dengan mengadaptasi basis kode saya ke props dan status readonly, saya menangkap beberapa masalah pengetikan yang tidak terdeteksi. Tidak ada keraguan bahwa mempublikasikan perubahan adalah pilihan yang tepat.

Patsisson, menggunakan VS, VS Code atau editor lainnya?

Maksud saya adalah bahwa sementara React mempromosikan pendekatan fungsional, ini memungkinkan alur kerja seperti videogame di mana Anda membuat perubahan pada dunia secara imperatif (status) dan kemudian merender (forceUpdate). Pendekatan ini sekarang dilarang di TS. Jenis func-damentalisme :)

Saya akan memikirkan alternatif untuk membuat ekosistem saya saat ini bisa diterapkan...

Masalah 2 melempar kesalahan hanya dengan strictNullChecks .

[TS] Cannot invoke an expression whose type lacks a call signature. Type '((val: any) => void) | undefined' has no compatible call signatures.

+1 Saya menggunakan strictNullChecks

@ericanderson ?

Seperti disebutkan di atas, ini terkait dengan perkakas, yang jelas di luar lingkup DT. Jika Anda menggunakan VSCode dan menginstal pratinjau parser TS yang akan datang, saya tidak melihat masalah ini dengan strictNullChecks ketika saya menulis salah satu di atas.

Saya tidak memiliki windows sehingga tidak dapat berbicara dengan VS dengan benar.

patch Pick ini merusak saran di bawah VSCode. ketika melakukan this.setState({ | }) (Ctrl + Spasi), tidak ada yang ditampilkan, meskipun Status didefinisikan dengan jelas dan menggunakan Partial<State> karena setState dapat mengatur status anggota secara selektif

IMHO kode yang benar seharusnya setState(state: Partial<S>, callback?: () => any): void;

Seperti yang dibahas di cabang permintaan tarik asli, kami mulai dengan Parsial. Namun jika objek status Anda adalah:

antarmuka Negara {
foo: string;
}

Kemudian dengan parsial Anda dapat melakukan hal berikut:

setState({foo: undefined});

Yang jelas salah.

Maaf - tapi sekali lagi tentang Readonly

Bereaksi bukan hanya Redux dan setState. Bereaksi juga mobx dan pola lain yang dapat diamati, di mana menetapkan properti negara adalah FITUR UTAMA . Perubahan yang dibahas sepenuhnya mematikan penggunaan mobx dengan TypeScript.

Jadi mengapa menambahkan perilaku yang tidak ada dalam kode reaksi asli ke file .d.ts? .d.ts harus menjadi cerminan dari perpustakaan asli tetapi bukan untuk mengajar orang tentang gaya pengkodean yang tepat, menurut sudut pandang penulis!

@lezious , saya khawatir saya tidak mengerti posisi Anda. Bisakah Anda memberikan contoh kode dan apa yang rusak tentang pengetikan sehubungan dengan sampel? Terima kasih!

Tidak masalah:

ini adalah kelas negara bagian saya

class UserInfoBlockState  
{
    <strong i="7">@observable</strong>                  <- this is mobx way to declare state
    public updating: boolean;
    <strong i="8">@observable</strong> 
    public deleted: boolean;
}

dan ini adalah komponen saya

<strong i="12">@observer</strong>       <-- this is mobx way to make component react to state change
export class UserPanel extends React.Component<IUserInfoBlockProps, UserInfoBlockState>
{
   ......
     private updateUser()
    {
        this.state.updating = true;
        UsersAPI.update(this.props.user)
       .then(() =>
            {
                this.state.updating = false;      <--- this is the mobx way to work with the state
            }
        ).catch(() =>
            {
                this.showErrror("Server error");
                this.state.updating = false;
            });
    }
   ....

}

dan sekarang kami (perusahaan kami dengan proyek besar yang ditulis di react+mobx) memperbarui DT dan React pada awal lingkaran rilis baru dan ... 3000+ kesalahan kompilasi "properti hanya dapat dibaca". Wow. Apa yang Anda sarankan untuk saya lakukan - menulis ulang seluruh proyek ke redux, tidak pernah memperbarui react.d.ts atau selalu menyimpan dan mendukung versi bercabang?

@mweststrate , tolong periksa ini.

@Iezious Saya menghargai posisi Anda dan saya meminta Anda untuk tenang. Saya mencoba untuk bekerja dengan Anda. Ini tidak ada hubungannya dengan Redux, tapi murni React.

Saya tidak ingin DT memblokir kasus penggunaan yang telah berfungsi sebelumnya, namun saya tidak berpikir bagaimana Anda menggambarkan penggunaan mobx dengan reaksi konsisten dengan dokumentasi reaksi (atau bahkan dokumentasi mobx sekarang setelah saya membaca dia).

Bereaksi dengan jelas menyatakan dalam dokumentasi bahwa ada 3 cara untuk menggunakan status dengan benar, dan yang pertama adalah "Jangan ubah status secara langsung".

Ini membuat saya percaya bahwa cara basis kode Anda saat ini bekerja kemungkinan besar akan rusak di versi reaksi yang akan datang. Dalam mencari melalui https://github.com/mobxjs/mobx-react , saya tidak melihat saran apa pun agar Anda menggunakan status dengan cara ini. Bahkan, sepertinya mereka ingin Anda menggunakan properti.

Meninjau https://mobx.js.org/getting-started.html dan mencari di googling untuk "status reaksi mobx", saya gagal menemukan dokumentasi yang menyarankan Anda menggunakan mobx seperti yang Anda lakukan.

DT seharusnya menyampaikan semangat perpustakaan yang mendasari yang terbaik dan implementasi aktual yang terburuk dan jelas bahwa membeli ke dalam bereaksi dan memperluas komponen berarti menghormati kontrak tersirat.

Saya tidak yakin apa yang saya sarankan Anda lakukan. Beberapa opsi yang dapat saya pikirkan begitu saja:

  1. Opsi "murah", jika Anda bersikeras untuk mengambil alih variabel state adalah mencari dan mengganti React.Component dengan MyComponent dan mendefinisikan MyComponent sebagai subkelas dari React.Component tanpa batasan readonly.
  2. Lain, berdasarkan contoh idiomatik yang diposting dalam dokumentasi untuk mobx adalah meluangkan waktu untuk berhenti menggunakan this.state dan hanya menggunakan variabel pada React.Component yang sebenarnya. Ini mungkin sedikit menyakitkan tetapi setidaknya orang baru di proyek Anda akan dapat melihat pola dalam basis kode Anda seperti yang dijelaskan secara online.
  3. Anda dapat mendeklarasikan ulang state di setiap komponen jika Anda ingin tetap melakukannya seperti yang Anda lakukan.
  4. Anda dapat mencari dan mengganti this.state dengan this.somethingElse dan mendeklarasikannya secara manual.
  5. Anda dapat berhenti mengambil pembaruan untuk bereaksi dari DT (dan mungkin di masa mendatang dari reaksi secara umum tergantung pada bagaimana perubahan di masa mendatang dapat memengaruhi kasus penggunaan Anda).

Jika itu adalah proyek saya, saya mungkin akan melakukan nomor 2, meskipun saya tidak cukup tahu tentang mobx untuk mengetahui dengan pasti.

Maaf saya tidak bisa setuju dengan Anda (tidak berarti orang lain tidak akan setuju dengan Anda). Saya mencoba mencari alasan untuk mengembalikan bagian itu tetapi sepertinya saya tidak bisa masuk saat ini.

Saya akan menyebutkan lagi strategi kami, yang merupakan aplikasi kustom dari RxJs yang dapat diamati untuk mendorong perubahan status React dan rendering yang sangat mirip dengan pola mobx. Kami menggunakan tindakan untuk menerima masukan dari lapisan tampilan (Bereaksi). Tindakan identik dengan fungsi yang menggunakan input dan menghasilkan yang dapat diamati, yang selanjutnya akan mendorong keadaan lain yang dapat diamati. Pola ini memungkinkan status untuk tetap tidak berubah dari perspektif lapisan React, karena Anda hanya pernah meminta nilai yang dapat diamati dan menjalankan tindakan status. Secara internal, status Anda mungkin "bermutasi" sebagai akibat dari tindakan tersebut, karena status internal tidak memiliki batasan hanya-baca.

Aku tidak bisa tenang. Saya memiliki proyek dalam produksi dan saya perlu menghabiskan banyak waktu tim, dan ini berarti uang sejak perubahan ini.

Dan apa alasannya? Apakah perubahan ini mencerminkan kenyataan dalam reaksi? Tidak. Ia menambahkan pembatasan dan perilaku yang tidak ada dalam reaksi, pembatasan yang ditambahkan entah bagaimana hanya karena dia berpikir bahwa ini benar.

Apa tujuan dari proyek DT? Untuk menggambarkan perpustakaan JS seakurat mungkin, atau menggambarkan visi kami tentang cara penggunaan yang benar dari perpustakaan tersebut? Sesuai dengan nama "Pasti Diketik" itu yang pertama. Jadi, jika pembatasan ini tidak ada di perpustakaan JS asli, itu TIDAK HARUS ada di DT juga. Ini adalah poin saya. Di mana saya salah dalam hal ini?

Saya mengerti Anda frustrasi, namun, ini adalah cara reaksi yang dimaksudkan untuk digunakan dengan membaca dokumen. Saya hampir tidak mengerti bagaimana kita harus membuat DT kurang spesifik karena tim Anda menyalahgunakan perpustakaan dan melanggar kontrak tersirat.

Tunjukkan pada saya satu ons dokumentasi yang dikeluarkan oleh tim reaksi yang menyarankan bahwa status harus dapat diubah secara langsung dan saya akan segera mengubah kode kembali.

https://facebook.github.io/react/docs/react-component.html#state

Jangan pernah mengubah this.state secara langsung, karena memanggil setState() setelahnya dapat menggantikan mutasi yang Anda buat. Perlakukan this.state seolah-olah tidak dapat diubah.

Tampaknya cukup jelas bahwa reaksi menganggap this.state tidak dapat diubah. React tidak menganggap _properties_ dari this.state tidak dapat diubah (yang merupakan asumsi redux). Anda bebas melakukan:

this.state.user.name = "foo";

dalam reaksi idiomatik.

Preferensi saya adalah mengetikkan API secara akurat (yang dalam hal ini berarti Readonly ) dan mengekspresikan semua invarian yang dinyatakan oleh tim reaksi.

@ericanderson maaf saya baru menyadari ini. FWIW Saya pikir perubahan itu masuk akal dan perkakas itu akan menyusul. Sebagai oleh oleh, pernahkah Anda mendengar mereka mempertimbangkan untuk menghentikan kelebihan setState yang mengambil objek? Masa depan adalah gaya pengurangan setState oleh semua akun.

@amoreland Tidak setuju. Per: https://facebook.github.io/react/docs/state-and-lifecycle.html#do -not-modify-state-directly

Jangan Ubah Status Secara Langsung

Misalnya, ini tidak akan merender ulang komponen:

// Wrong
this.state.comment = 'Hello';

Sebagai gantinya, gunakan setState():

// Correct
this.setState({comment: 'Hello'});

Satu-satunya tempat di mana Anda dapat menetapkan this.state adalah konstruktor.

@johnnyreilly saya belum. Itu menarik. Sumber?

Itu dibahas dalam salah satu pembicaraan dalam konferensi reaksi baru-baru ini. Ini tersedia di YouTube. Itu mungkin pembicaraan oleh Lin Clark. Salah satu pembicaraan pertama pada hari 1 - membahas perubahan yang akan datang untuk bereaksi terhadap v16. serat dll

Maaf @gaearon maksud saya

Alasan mengapa mobx melakukan perubahan status, adalah karena yang dapat diamati mendorong pembaruan React, alih-alih _mengganti_ status sepenuhnya, status menjadi mesin yang mendorong rendering. jadi dengan melakukan sesuatu seperti this.state.updating = true; Anda sebenarnya melakukan hal yang setara dengan penggantian status, tetapi status cukup pintar untuk memicu render baru dengan memberi tahu komponen bahwa status diubah dari konten sebelumnya. Saya setuju bahwa ini bukan penggunaan React _konvensional_, melainkan penggunaan React yang jauh lebih komprehensif dan tingkat yang lebih tinggi. Saya berpendapat bahwa penggunaan React konvensional hanya berguna untuk proyek kecil dengan beberapa komponen. Ketika Anda berakhir dengan 100-an komponen masing-masing dengan beberapa lusinan driver mutasi reaktif, Anda tidak lagi dapat dengan andal menggunakan perubahan status React konvensional (yaitu, setState) dan harus ada untuk menghibur perubahan arsitektur yang melibatkan status _smart_ (yang pada dasarnya dilakukan oleh mobx ).

Jadi, saya mengerti mengapa dia kesal, karena perubahan pengetikan memengaruhi penggunaan sistem React yang lebih maju. Penetapan status yang dapat diamati secara teknis bukan status _mutating_, melainkan memanggil peristiwa yang dapat diamati untuk nilai RHS. Kebetulan sintaks yang dipilih mobx untuk mengeluarkan peristiwa yang dapat diamati ini bertentangan dengan maksud tersurat dari status React yang tidak dapat diubah.

@Iezious jika Anda memerlukan perbaikan cepat untuk jenis masalah ini, Anda dapat mengatasinya dengan menambahkan pengetikan React dan memfaktorkan ulang komponen Anda untuk menggunakan ekstensi ke defs tipe React.Component .

import * as React from 'react';
declare module 'react' {
  class MutableStateComponent<P, S> extends React.Component<P, S> {
    state: S;
  }
}
(React as any).MutableStateComponent = React.Component;

dan sekarang Anda bisa membuat komponen status yang dapat diubah seperti komponen biasa, kecuali anggota state mereka tidak lagi ditandai sebagai readonly .

export class MyComponent extends React.MutableStateComponent<MyProps, MyState> {
  // this.state.name is writable
}

@patsissons ya, itulah alasannya.

Saya tidak mengubah status, saya menggunakan mobx observables, yang memanggil setState untuk saya, saya melakukannya karena kode proyek BESAR saya terlihat jauh lebih jelas dan dapat dimengerti.

Saya tahu bahwa saya dapat membuat komponen saya, saya juga dapat membuat skrip di server npm saya yang akan selalu mengembalikan perubahan ini untuk perusahaan kami. Saya dapat menggunakan peretasan seperti "this.state.state.updated" dan banyak peretasan lainnya.

Saya hanya ingin mengatakan, bahwa perubahan ini memengaruhi penggunaan pola yang dapat diamati seperti mobx, yang pada kenyataannya mengikuti cara reaksi, tetapi sekarang, karena perubahan ini tidak dapat dikompilasi dan memerlukan beberapa peretasan dan penyelesaian untuk bekerja. Dan itulah mengapa saya berpikir bahwa perubahan ini tidak benar.

mungkin daripada MutableStateComponent yang saya sarankan, kami malah menyebutnya ObservableComponent yang lebih selaras dengan pola React yang Dapat Diobservasi.

Jika Anda menjatuhkan Readonly maka orang yang menggunakan tipe reaksi dengan reaksi biasa (dan/atau sejumlah sistem manajemen negara selain dari mobx) terkena kesalahan. Saya tidak menggunakan mobx dalam proyek besar saya dan saya menghargai kesalahan kompiler ketika seseorang membuat kesalahan ketik dan secara tidak sengaja menggunakan this.state.foo = bar .

Jika ada tradeoff yang tidak dapat dihindari antara reaksi standar dan penggunaan reaksi non-standar, jenis reaksi standar harus condong ke yang pertama.

Selanjutnya, jika Anda menggunakan mobx dengan cara idiomatis, ini bukan masalah.

Jika Anda menjatuhkan Readonly maka orang-orang yang menggunakan tipe reaksi dengan reaksi biasa (dan/atau sejumlah sistem manajemen negara selain mobx) terkena kesalahan. Saya tidak menggunakan mobx dalam proyek besar saya dan saya menghargai kesalahan kompiler ketika seseorang membuat kesalahan ketik dan secara tidak sengaja menggunakan this.state.foo = bar.

Jadi sekali lagi - Anda MENGAJAR UNTUK MENULIS KODE

Proyek ini bukan tentang mengajar menulis kode, proyek ini untuk menggambarkan fungsionalitas yang ada dari perpustakaan JS. Batasan yang dibahas tidak ada di perpustakaan asli dan harus dihapus.

Itu saja.

@patsissons

jika Anda membutuhkan perbaikan cepat untuk jenis masalah ini, Anda dapat mengatasinya dengan menambah pengetikan React dan memfaktorkan ulang komponen Anda untuk menggunakan ekstensi ke defs jenis React.Component.

tidak benar. Di dunia perusahaan, dari tempat saya berada, tidak ada "perbaikan cepat". Ketika sesuatu berubah di SDK itu atau HARUS kompatibel ke belakang, atau butuh bertahun-tahun. Tidak ada perbaikan cepat di 2M+ baris proyek kode. Ini adalah minggu-minggu perubahan, pengujian, pengujian produk A/B, peluncuran untuk sejumlah besar orang. Biayanya uang nyata. Dan semua upaya besar ini hanya karena seseorang menambahkan perubahan yang tidak kompatibel ke belakang yang TIDAK ADA DI PERPUSTAKAAN NYATA?

Menurut Anda mengapa forceUpdate masih ada di reaksi? Mereka berbicara tentang confs tentang gaya yang tepat, tetapi membuat perubahan untuk mencegah penggunaan cara lain. Mengapa? Karena ini adalah perusahaan besar dan mereka tahu bahwa perpustakaan harus kompatibel ke belakang.

@ericanderson menulis itu

this.state.state.value = 1 

tidak sah juga dari sudut pandangnya. Lain kali dia akan mendapatkan alat dari TS dan menambahkan batasan yang akan mencegah kode ini. Atau buat kelas akhir komponen, atau yang lainnya hanya karena "gayanya tepat dan mencegah orang membuat kesalahan".

Mencegah orang dari kesalahan - itu tugas FB, jika mereka ingin melakukannya, mereka dapat dengan mudah menambahkan proxy dan melarang mutasi negara. Tujuan DT adalah menambahkan deskripsi, dan tidak ada yang lain.

@Iezious

Saya pikir intinya adalah bahwa tim React tidak dapat membuat status tidak dapat diubah dengan JavaScript, tetapi jika mereka bisa, mereka akan melakukannya. TypeScript di sisi lain dapat membuat status tidak dapat diubah, jadi itulah mengapa perubahan ini dilakukan pada definisi tipe. Itu benar-benar maksud dari tim React untuk melarang modifikasi ke anggota negara secara langsung (seperti yang dibuktikan oleh dokumen resmi mereka tentang penggunaan state dengan benar ), tetapi mereka tidak memiliki konstruksi bahasa untuk memaksakan batasan itu. Kendala ini tidak pernah diketahui, telah didokumentasikan dengan baik dari awal. Bagi kita yang beroperasi di luar penggunaan _konvensional_ React, setidaknya kita harus mematuhi rekomendasi penggunaan resmi React. Bagi tim saya, itu berarti merancang solusi yang memungkinkan kami mendorong perubahan status tanpa mengubah status secara langsung. Ini secara khusus dilakukan untuk memastikan bahwa kami tidak akan menghadapi masalah seperti ini (walaupun perubahan ini sedikit memengaruhi kami, tetapi hanya melalui tanda tangan fungsi dan bukan desain mendasar).

Jika refactoring tidak memungkinkan dalam situasi Anda, maka pin @types/react di 15.0.1 sebelum perubahan dilakukan, atau jangan gunakan @types/react dan sebagai gantinya pertahankan varian Anda sendiri dari ketik defs dan cukup ubah pengetikan state menjadi Component . Saya benar-benar tidak berpikir Anda akan berhasil meyakinkan siapa pun untuk mengembalikan perubahan.

forceUpdate ada karena didokumentasikan sebagai jalur kode yang direkomendasikan untuk mendorong rendering saat status struktural internal diubah (atau saat render() menggunakan data di luar status yang dapat diubah). Penggunaan forceUpdate dirancang untuk persis pola penggunaan yang tim saya gunakan dengan React.

Tim React BISA membuat status tidak berubah dengan JS, Sangat mudah. Tapi tidak kompatibel ke belakang.

Sekali lagi, adalah:

this.state.state.value = 1 

sah atau tidak?

Saya pikir itu, tapi pendapat saya sudah jelas ...

Saya tidak berpikir percakapan tentang mutabilitas/kekekalan tidak relevan _yet_. Jelas bug di kompiler TS (mengenai Readonly ) dikombinasikan dengan perubahan ini membuat komponen generik sama sekali tidak mungkin digunakan, merusak banyak kode yang ada. Tentunya itu adalah kasus penggunaan valid yang diterima secara universal dan apakah cukup alasan untuk membatalkannya untuk saat ini???

interface test1 {
    num: number;
}

function add<T extends test1>(initial: T, add: number) {
    var copy: Readonly<T> = initial;

    //ERROR HERE: [ts] Operator '+' cannot be applied to types 'T["num"]' and 'number'.
    return copy.num + add;
}

Adakah yang tahu jika ada masalah terbuka dengan tim TypeScript tentang ini? Sepertinya saya tidak dapat menemukan masalah yang relevan di pelacak mereka.

@caesay Lihat Microsoft/TypeScript#15501

saya harus init state di konstruktor, tetapi tslint menunjukkan "state is readonly"....

constructor(props) {
  super(props);
  this.state = {
     value: props.defaultValue,
  };
}

bagaimana saya bisa memperbaikinya ...............

Gunakan setState

setState tidak berfungsi di konstruktor

Atau pertimbangkan componentWillMount w/setState

Terima kasih

Halo,

Saya membaca seluruh utas tetapi tidak jelas bagi saya apakah ada rencana untuk menangani skenario @alanwei0 ?

Saya sepenuhnya setuju bahwa masuk akal untuk memiliki state sebagai ReadOnly . Dikatakan bahwa tidak dapat mengatur keadaan awal dalam konstruktor memperumit banyak hal karena render dipanggil sebelum componentDidMount selesai.

@pawelpabich menggunakan this.state = { di konstruktor tidak menjadi masalah. Anda dapat menetapkan variabel readonly dalam konstruktor, dan Readonly<T> tidak mencegah penugasan (selalu!).

interface TInterface {
    test: string;
}

class TClass {
    readonly blah: Readonly<TInterface>;
    constructor() {
        this.blah = { test: "constructor" };
    }

    fn = () => {
        this.blah = { test: "fn" };
    }
}

Satu-satunya kesalahan di sini akan berada di dalam fn - bukan karena Readonly<T> , tetapi karena kata kunci readonly . Faktanya, versi terbaru dari pengetikan bahkan tidak menggunakan kata kunci readonly , jadi Anda sebenarnya dapat menetapkan status di mana saja, hanya saja tidak mengubah properti di dalam status.

Masalah yang kita bicarakan di sini adalah bug dengan kompiler TypeScript yang menyebabkan properti negara kehilangan tipenya di komponen yang diwarisi. Ini telah diperbaiki, saya percaya, dan jika demikian, masalah ini dapat ditutup.

@caesay maaf, saya pikir itulah yang sedang kita bicarakan di sini. Masalah saya adalah saya tidak dapat mengatur status di kelas dasar. Saya di TS 2.4.1. Apakah Anda kebetulan id masalah sehingga saya dapat memeriksanya?

Anda selalu dapat memanggil setState (di componentWillMount).

@pawelpabich Sekali lagi, ini bukan masalah :) Anda tidak dapat menetapkan status dari kelas dasar dengan sengaja . Bagaimana Anda bisa? Anda tidak tahu kontrak prop yang digunakan oleh komponen turunan.

interface BaseCompState {
    baseState1?: string;
}

class BaseComp<TState extends BaseCompState> extends React.Component<any, TState> {
    constructor(props) {
        super(props);
        this.state = {
            baseState1: "fromBase",
        };
    }
}

interface TopCompState extends BaseCompState {
    topState1?: string;
}

class TopComp extends BaseComp<TopCompState> {
    constructor(props) {
        super(props);
        this.state = {
            baseState1: "fromTop",
            topState1: "fromTop",
        };
    }
}

Itu adalah contoh sederhana dari komponen turunan (alat peraga dihilangkan, tetapi ide yang sama). this.state = di kelas dasar jelas tidak bisa bekerja, karena tidak tahu apa itu TState . lebih jauh lagi, jika _did_ bekerja, maka itu hanya akan menimpa status yang ditetapkan oleh induknya. Status terakhir adalah { baseState1: "fronBase" } . Apa yang terjadi dengan properti topState?

Jika Anda benar-benar yakin bahwa Anda perlu menangani status di komponen dasar, Anda dapat meneruskan status dari komponen turunan ke konstruktor komponen dasar (sebagai TState sehingga Anda dapat menetapkannya) - yang mungkin terlihat seperti ini:

interface BaseCompState {
    baseState1?: string;
}

class BaseComp<TState extends BaseCompState> extends React.Component<any, TState> {
    constructor(props, state: TState) {
        super(props);
        this.state = Object.assign({
            baseState1: "fromTop",
        }, state);
    }
}

interface TopCompState extends BaseCompState {
    topState1?: string;
}

class TopComp extends BaseComp<TopCompState> {
    constructor(props) {
        super(props, {
            topState1: "fromTop",
        });
    }
}

Atau bahkan lebih sederhana lagi, Anda dapat memanggil this.setState( dari dalam komponen dasar Anda (ya, Anda dapat melakukannya di konstruktor!)

Tidak ada masalah di sini.

@caesay Saya setuju sepenuhnya bahwa jika tidak ada kendala maka penugasan tidak masuk akal. Tetapi kode berikut masih menghasilkan kesalahan kompilasi meskipun kompiler memiliki semua informasi yang diperlukan untuk memverifikasi bahwa kode tersebut benar.

import * as React from "react";

/* tslint:disable:max-classes-per-file*/

interface BaseProps {
    baseProp: string;
}

interface BaseState {
    baseState: string;
}

class Base<TProps extends BaseProps, TState extends BaseState> extends React.Component<TProps, TState> {
    constructor(props) {
        super(props);

        this.state = {
            baseState: props.baseProp
        };
    }

    render() {
        return <div>{this.state.baseState}</div>;
    }
}

interface DerivedProps extends BaseProps {
    derivedProp: string;
}

interface DerivedState extends BaseState {
    derivedState: string;
}

export class Derived extends Base<DerivedProps, DerivedState> {
    constructor(props) {
        super(props);

        this.state = {
            derivedState: props.derivedProp
        };
    }

    render() {
        return <div>{this.state.derivedState}</div>;
    }
}

kesalahan

webpack: Compiled successfully.
ERROR at Test.tsx(17,9):
TS2322: Type '{ baseState: any; }' is not assignable to type 'Readonly<TState>'.

ERROR at Test.tsx(39,9):
TS2322: Type '{ derivedState: any; }' is not assignable to type 'Readonly<DerivedState>'.
  Property 'baseState' is missing in type '{ derivedState: any; }'.

Version: typescript 2.4.1

Pertama. Alat peraga Anda di pangkalan di konstruktor tidak diketik. Jadi props.baseProp adalah any , yang tidak dapat dialihkan!

Kedua, alat peraga Anda di Derived memiliki masalah yang sama DAN Anda kehilangan baseState . Yang tentu saja tidak akan berfungsi, terlepas dari Readonly

TProps extends BaseProps yang berarti TProps memiliki setidaknya anggota yang sama dengan BaseProps . Jadi bagaimana itu tidak didefinisikan? Saya mengerti bahwa kompiler mungkin tidak dapat menyimpulkannya tetapi mengatakan bahwa tidak ditentukan sepertinya tidak benar. Pemikiran yang sama dapat diterapkan pada Derived .

@caesay setState di konstruktor bukanlah solusi yang dapat diandalkan karena metode ini async dan Anda masih bisa mendapatkan render tanpa set status awal.

Satu-satunya solusi andal yang bisa saya lihat adalah mengatur seluruh status di kelas turunan. Ini memiliki kelemahan yang jelas bahwa:

  1. Ini perlu diduplikasi di setiap kelas turunan
  2. Kelas turunan perlu tahu tentang status yang tidak mereka pedulikan.

Contoh yang saya tunjukkan di atas akan bekerja di C# jadi alangkah baiknya jika bisa bekerja di TypeScript.

  1. ~setState aman di konstruktor~
  2. Dalam kasus kelas turunan, kasus terbaik yang bisa saya lihat adalah memanggil setState di componentWillMount Anda. Basis Anda tidak tahu apa yang harus ada dalam status anaknya, sehingga tidak mungkin memasukkan this.state ke dalam konfigurasi yang aman. Namun subkelas Anda dapat memanggil componentWillMount induknya dan kemudian mengatur status apa pun yang menurutnya juga dibutuhkan.
  3. Ada fitur bahasa dalam banyak bahasa yang bagus untuk dimiliki di TypeScript. Beberapa layak. Lainnya tidak. Either way itu bukan argumen untuk inklusi mereka.
  4. Kesalahan yang Anda lihat masuk akal. Mereka tidak menyarankan bug dalam TypeScript atau pengetikan. Dalam setiap kasus, Anda benar-benar mencoba menetapkan this.state dengan objek yang tidak cocok dengan tipe yang diharapkan.

DIEDIT, untuk mencerminkan bahwa saya salah tentang setState di konstruktor dan untuk menambahkan backticks agar mudah dibaca.

@ericanderson Saya tidak berpikir kami membuat kemajuan apa pun di sini. Saya menunjukkan kepada Anda sebuah contoh dan saya akan menghargai jika Anda dapat fokus pada hal itu. Kalau tidak, sulit untuk berdiskusi.

Kembali setState tidak aman untuk digunakan dalam konstruktor. Itu tidak menimbulkan kesalahan tetapi tidak akan mengatur status sebelum render terjadi. Saya memiliki kode yang rusak karena itu dan dokumen React sangat jelas bahwa tidak boleh digunakan di sana.

@pawelpabich Tidak, ini bukan tempat untuk berdebat. Anda pada dasarnya salah dalam pemahaman Anda tentang bahasa tersebut. Dalam contoh yang Anda berikan, Anda belum memenuhi kontrak "Negara Bagian" di SALAH SATU dari tugas Anda ke negara bagian.

Misalnya, ketika Anda melakukannya

this.state = { baseState: props.baseProp };
// now the state is exactly { baseState: props.baseProp }

Statusnya persis { baseState: props.baseProp } dan ini TIDAK memenuhi persyaratan TProps extends BaseProps (karena kami tidak tahu apa itu TProps! ia bisa memiliki properti apa pun di dalamnya).

Setelah itu, Anda melakukan tugas _ TERPISAH _.

this.state = { derivedState: props.derivedProp };
// now the state is exactly {  derivedState: props.derivedProp }

statusnya sekarang persis { derivedState: props.derivedProp } (Anda telah menimpa penetapan status dasar Anda!!) dan ini tidak memenuhi DerivedState OR BaseProps.

Anda benar-benar sesat dalam berpikir bahwa ini seharusnya berhasil. Jika Anda memiliki masalah dengan cara kerja penugasan variabel dasar, selesaikan dengan desainer bahasa dalam edisi baru dan tembak di sana sehingga kami berhenti menerima pemberitahuan di sini.

Sebagai catatan tambahan - Anda JUGA mengganti metode render() dasar Anda, yang berarti komponen dasar Anda tidak akan dapat merender apa pun. Jika Anda yakin ini yang Anda inginkan, Anda dapat menambahkan anggota yang dilindungi getBaseState() dan memanggil ini dari konstruktor turunan saat Anda menyetel status (sehingga Anda tidak perlu menduplikasi logika status dasar). Tapi apa yang saya pikir Anda benar-benar inginkan adalah tidak menggunakan komponen turunan sama sekali. Coba restrukturisasi untuk menggunakan komposisi (di mana Anda memiliki satu objek yang berisi beberapa objek anak). Saya pikir Anda akan menemukan itu akan menjadi jauh lebih bersih.

Saya hampir tidak ingin menjauh dari membaca untuk membuat proyek baru hanya untuk berdebat dengan Anda, tapi sayangnya...

Saya akan dikoreksi tentang setState () di konstruktor, tetapi itu tidak mengubah perasaan saya tentang menggunakannya di componentWillMount .

Contoh kerja tentang bagaimana ini akan dilakukan:
https://github.com/ericanderson/set-state-example

Secara khusus, index.tsx:

import * as React from "react";
import * as ReactDOM from "react-dom";

/* tslint:disable:max-classes-per-file*/

interface BaseProps {
  baseProp: string;
}

interface BaseState {
  baseState: string;
}

class Base<TProps extends BaseProps, TState extends BaseState> extends React.Component<TProps, TState> {
  public componentWillMount() {
    this.setState({
      baseState: this.props.baseProp,
    });
  }

  public render() {
    return (
      <p>
        <code>this.state.baseState: </code>
        {this.state.baseState}
      </p>
    );
  }
}

interface DerivedProps extends BaseProps {
  derivedProp: string;
}

interface DerivedState extends BaseState {
  derivedState: string;
}

export class Derived extends Base<DerivedProps, DerivedState> {
  public componentWillMount() {
    super.componentWillMount();
    this.setState({
      derivedState: this.props.derivedProp,
    });
  }

  public render() {
    return (
      <div>
        <p>
          <code>this.state.derivedState: </code>
          {this.state.derivedState}
        </p>
        {super.render()}
      </div>
    );
  }
}

ReactDOM.render(<Derived derivedProp="its derived" baseProp="its basic" />, document.getElementById("main"));

@pawelpabich jika Anda ingin mengimplementasikan komponen polimorfik dengan status awal, Anda perlu membuat komponen dasar abstrak dan membuat fungsi getInitialState() abstrak (atau bertema serupa) untuk diterapkan di kelas turunan Anda. Anda hanya ingin menetapkan status satu kali, baik di konstruktor atau dengan setState seperti yang ditunjukkan @ericanderson .

di bawah ini adalah contoh Anda yang dikonversi ke solusi polimorfik sepenuhnya, dengan pemisahan masalah yang lengkap sehubungan dengan konstruksi keadaan:

interface BaseProps {
  baseProp: string;
}

interface BaseState {
  baseState: string;
}

abstract class Base<TProps extends BaseProps, TState extends BaseState> extends React.Component<TProps, TState> {
  constructor(props: TProps) {
      super(props);

      this.state = this.getInitialState();
  }

  protected abstract getInitialState(): TState;

  protected getBaseState() {
    return this.props.baseProp;
  }

  render() {
      return <div>{this.state.baseState}</div>;
  }
}

interface DerivedProps extends BaseProps {
  derivedProp: string;
}

interface DerivedState extends BaseState {
  derivedState: string;
}

export class Derived extends Base<DerivedProps, DerivedState> {
  getInitialState(): DerivedState {
    return {
      baseState: this.getBaseState(),
      derivedState: this.props.derivedProp,
    };
  }

  render() {
      return <div>{this.state.derivedState}</div>;
  }
}

@patsissons terima kasih!

@caesay saya akui saya salah dan untuk beberapa alasan tidak melihat bahwa tugas akan saling menimpa. Meskipun demikian, penggunaan CAPS dan ! tidak membantu saya keluar dari lubang saya.

@patsissons dan @ericanderson fokus pada masalah dan sekarang kami memiliki solusi yang dapat digunakan orang lain.

@pawelpabich Saya setuju perilaku saya kurang profesional - tetapi dapat dimengerti, mengingat saya memberi Anda beberapa penjelasan, contoh, dll, dan Anda memilih untuk tidak mendengarkan saya.

maka itu hanya akan menimpa status yang ditetapkan oleh orang tua.

[_jika Anda ingin_] menangani status di komponen dasar Anda, Anda dapat meneruskan status dari komponen turunan ke konstruktor komponen dasar

[_jika Anda ingin menangani status dalam komponen turunan Anda_] Anda dapat menambahkan anggota yang dilindungi getBaseState() dan memanggil ini dari konstruktor turunan saat Anda menyetel status.

Apa yang dilakukan @patsissons adalah mengambil komentar yang telah disebutkan di sini dan memberikan contoh kode - yang seharusnya tidak diperlukan. Ini bukan stackoverflow, dan kami juga tidak sering memberikan contoh kode siap pakai di sana.

Saya baru bereaksi dan mengetik, mungkin saya tidak tahu, tetapi meskipun aplikasi dikompilasi tanpa kesalahan, peringatan dan petunjuk, saya mendapatkan kesalahan runtime. Di bawah ini adalah contoh komponen. Saya mengaitkan kesalahan dengan status Readonly . Jika aplikasi berfungsi sebelum perubahan Readonly , maka setelah perubahan ini aplikasi berhenti bekerja dan tidak memberikan kesalahan waktu kompilasi.

import * as React from 'react';

export default class HomePage extends React.Component<any, Map<string, string>> {

  public componentWillMount() {
    const map = new Map<string, string>();
    map.set('aKey', 'aValue');
    this.setState(map);
  }

  public render() {

      return (
        <div className="home">
          <div className="greeting">
            Home page: {this.state.get('aKey')} // <-- I get an error here
          </div>
        </div>
      );
  }
}

kesalahan:

homePage.tsx:12 Uncaught TypeError: this.state.get is not a function
    at HomePage.render (homePage.tsx:12)
    at eval (ReactCompositeComponent.js:793)
    at measureLifeCyclePerf (ReactCompositeComponent.js:73)
    at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (

Status harus selalu menjadi objek dengan kunci biasa afaik, jadi alih-alih tentukan status
sebagai sesuatu seperti: { values: Map <string, string> } dan baca
this.state.values.get('aKey')

Op vr 29 sep. 2017 om 09:01 schreef Janusz Białobrzewski <
[email protected]>:

Saya baru bereaksi dan mengetik, mungkin saya tidak tahu, tetapi bahkan
meskipun aplikasi dikompilasi tanpa kesalahan, peringatan dan petunjuk, saya mendapatkan runtime
kesalahan. Di bawah ini adalah contoh komponen. Saya menghubungkan kesalahan dengan Readonly
negara. Jika aplikasi berfungsi sebelum perubahan Readonly, maka setelah ini
mengubahnya berhenti bekerja dan tidak memberikan kesalahan waktu kompilasi.

import * sebagai Bereaksi dari 'bereaksi';
ekspor HomePage kelas default memperluas React.Component> {

komponen publikWillMount() {
peta const = Peta baru();
map.set('aKey', 'aValue');
this.setState(peta);
}

render publik() {

  return (
    <div className="home">
      <div className="greeting">
        Home page: {this.state.get('aKey')} // <-- I get an error here
      </div>
    </div>
  );

}
}

kesalahan:

halaman rumah. tsx:12 TypeError Tidak Tertangkap: this.state.get bukan fungsi
di HomePage.render (homePage.tsx:12)
di eval (ReactCompositeComponent.js:793)
di measureLifeCyclePerf (ReactCompositeComponent.js:73)
di ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/DefinitelyTyped/DefinitelyTyped/issues/14250#issuecomment-333047367 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/ABvGhM5hDyRNyUeZuIiGeTZk1N-rfuA4ks5snJW5gaJpZM4LuDWV
.

Terima kasih, tetapi tampaknya upaya yang sia-sia untuk mendeklarasikan status sebagai Readonly<S> , karena anggota bersarangnya tidak terpengaruh oleh Readonly.

Mungkin suatu hari nanti Readonly akan diterapkan secara rekursif, tetapi untuk saat ini, Anda harus memastikan bahwa Anda telah menanganinya dengan benar. Dalam kasus Anda, Anda harus benar-benar mendeklarasikan ReadonlyMap atau

interface State {
    readonly [key: string]: string;
}

atau bersarang:

interface State {
    map: { readonly [key: string]: string };
}

Kita dapat menggunakannya untuk membaca dalam saja:

export type DeepReadonly<T> =
  T extends Array<any> ?
  ReadonlyArray<T[0]> :
  T extends Date ?
  T :
  T extends Function ?
  T :
  T extends object ?
  { readonly [P in keyof T]: DeepReadonly<T[P]> } :
  T;

export type Writable<T> =
  T extends ReadonlyArray<any> ?
  Array<WritableObject<T[0]>> :
  T extends Array<any> ?
  Array<WritableObject<T[0]>> :
  WritableObject<T>;

type WritableObject<T> =
  T extends Date ?
  T :
  T extends Function ?
  T :
  T extends object ?
  { -readonly [P in keyof T]: Writable<T[P]> } :
  T;
Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

victor-guoyu picture victor-guoyu  ·  3Komentar

variousauthors picture variousauthors  ·  3Komentar

jbreckmckye picture jbreckmckye  ·  3Komentar

Loghorn picture Loghorn  ·  3Komentar

ArtemZag picture ArtemZag  ·  3Komentar