Tidak disebutkan-berikutnya: manajemen keadaan minimalis dari aplikasi Bereaksi

200 byte untuk pengelolaan status komponen Bereaksi

  • Bereaksi kait : itu semua yang diperlukan untuk mengelola negara.
  • ~ 200 byte , min + gz.
  • Familiar API : Cukup gunakan Bereaksi seperti biasa.
  • Minimum API : lima menit sudah cukup untuk mengetahuinya.
  • Ditulis dalam TypeScript untuk memberikan inferensi tipe otomatis.

Pertanyaan utamanya adalah: apakah paket ini lebih baik daripada Redux? Baiklah ...


  • Dia kurang. Ini 40 kali lebih kecil.
  • Dia lebih cepat. Mengisolasi masalah kinerja tingkat komponen.
  • Lebih mudah dipelajari. Bagaimanapun, Anda harus dapat menggunakan kait Bereaksi dan konteks, mereka keren.
  • Lebih mudah diintegrasikan. Hubungkan satu komponen pada satu waktu tanpa memutus kompatibilitas dengan perpustakaan Bereaksi lainnya.
  • Lebih mudah untuk diuji. Menguji reduksi secara terpisah adalah buang-buang waktu, sederhanakan menguji komponen Bereaksi sendiri.
  • Ini lebih sederhana dalam hal mengetik. Ini ditulis untuk memaksimalkan penggunaan inferensi tipe.
  • Dia minimalis. Ini hanya React.

Contoh kode


import React, { useState } from "react" import { createContainer } from "unstated-next" import { render } from "react-dom" function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <span>{counter.count}</span> <button onClick={counter.increment}>+</button> </div> ) } function App() { return ( <Counter.Provider> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) } render(<App />, document.getElementById("root")) 

Sikap terhadap yang tidak disebutkan


Saya (Jamie Kyle - kira-kira Per.) Pertimbangkan perpustakaan ini sebagai penerus Unstated . Saya melakukan Unstated karena saya yakin bahwa Bereaksi sendiri melakukan pekerjaan besar dalam mengelola negara, dan tidak memiliki mekanisme sederhana untuk memisahkan keadaan umum dan logika. Oleh karena itu, saya membuat Unstated sebagai solusi "minimum" untuk masalah ini.


Dengan munculnya kait, Bereaksi telah menjadi jauh lebih baik dalam hal menyoroti keadaan umum dan logika. Jauh lebih baik dari sudut pandang saya, Unstated telah menjadi abstraksi yang tidak perlu.


BUKAN KURANG , saya percaya bahwa banyak pengembang tidak tahu cara memisahkan logika dan keadaan umum aplikasi menggunakan React hooks. Ini mungkin semata-mata karena kurangnya kualitas dokumentasi dan kelembaman komunitas, tetapi saya percaya bahwa API yang jelas dapat memperbaiki kekurangan ini.


Unstated Next adalah API ini. Alih-alih menjadi "API Minimum untuk berbagi negara dan logika di Bereaksi," sekarang memiliki "API Minimum untuk memahami cara berbagi keadaan dan logika di Bereaksi."


Saya sangat suka Bereaksi, saya ingin Bereaksi berkembang. Saya lebih suka bahwa komunitas meninggalkan penggunaan perpustakaan eksternal untuk mengelola negara seperti Redux, dan akhirnya mulai menggunakan alat yang dibangun menjadi Bereaksi dengan kekuatan penuh.


Jika, alih-alih menggunakan Unstated, Anda hanya menggunakan React - Saya akan menyambut ini. Tulis tentang itu di blog Anda! Bicara tentang itu di konferensi! Bagikan pengetahuan Anda dengan komunitas.


Panduan Tidak Tercantum-berikutnya


Jika Anda belum terbiasa dengan React hooks, saya sarankan Anda berhenti membaca dan membaca
dokumentasi yang sangat baik di situs web Bereaksi .


Jadi, dengan bantuan kait Anda dapat menulis sesuatu seperti komponen ini:


 function CounterDisplay() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return ( <div> <button onClick={decrement}>-</button> <p>You clicked {count} times</p> <button onClick={increment}>+</button> </div> ) } 

Jika komponen logika perlu digunakan di beberapa tempat, itu bisa diambil
ke dalam pengait khusus yang terpisah:


 function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } function CounterDisplay() { let counter = useCounter() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } 

Tetapi apa yang harus dilakukan ketika Anda membutuhkan kondisi umum, dan bukan hanya logika?
Konteksnya berguna di sini:


 function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContext(null) function CounterDisplay() { let counter = useContext(Counter) return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } function App() { let counter = useCounter() return ( <Counter.Provider value={counter}> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) } 

Itu luar biasa dan luar biasa; semakin banyak orang menulis dengan gaya ini, semakin baik.


Namun, ada baiknya menambahkan sedikit lebih banyak struktur dan kejelasan sehingga API mengekspresikan niat Anda dengan sangat jelas.


Untuk melakukan ini, kami menambahkan fungsi createContainer() , sehingga Anda dapat memperlakukan kait khusus Anda sebagai "wadah", sehingga API kami yang jelas dan jelas tidak mungkin digunakan secara salah.


 import { createContainer } from "unstated-next" function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } function App() { return ( <Counter.Provider> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) } 

Bandingkan teks komponen sebelum dan sesudah perubahan kami:


 - import { createContext, useContext } from "react" + import { createContainer } from "unstated-next" function useCounter() { ... } - let Counter = createContext(null) + let Counter = createContainer(useCounter) function CounterDisplay() { - let counter = useContext(Counter) + let counter = Counter.useContainer() return ( <div> ... </div> ) } function App() { - let counter = useCounter() return ( - <Counter.Provider value={counter}> + <Counter.Provider> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) } 

Jika Anda menulis dalam TypeScript (dan jika tidak, saya sangat menyarankan Anda membiasakan diri dengannya), Anda juga akan mendapatkan inferensi jenis yang lebih baik. Jika kait khusus Anda diketik dengan kuat, output dari semua tipe lainnya akan bekerja secara otomatis.


API


createContainer(useHook)


 import { createContainer } from "unstated-next" function useCustomHook() { let [value, setValue] = useState() let onChange = e => setValue(e.currentTarget.value) return { value, onChange } } let Container = createContainer(useCustomHook) // Container === { Provider, useContainer } 

<Container.Provider>


 function ParentComponent() { return ( <Container.Provider> <ChildComponent /> </Container.Provider> ) } 

Container.useContainer()


 function ChildComponent() { let input = Container.useContainer() return <input value={input.value} onChange={input.onChange} /> } 

useContainer(Container)


 import { useContainer } from "unstated-next" function ChildComponent() { let input = useContainer(Container) return <input value={input.value} onChange={input.onChange} /> } 

Kiat


Kiat # 1: Menggabungkan Kontainer


Karena kita berurusan dengan kait khusus, kita dapat menggabungkan wadah di dalam kait lain.


 function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment, setCount } } let Counter = createContainer(useCounter) function useResettableCounter() { let counter = Counter.useContainer() let reset = () => counter.setCount(0) return { ...counter, reset } } 

Kiat # 2: Gunakan Kontainer Kecil


Kontainer sebaiknya dibuat kecil dan jelas difokuskan pada tugas tertentu. Jika Anda memerlukan logika bisnis tambahan dalam wadah - keluarkan operasi baru dalam kaitan terpisah, dan biarkan negara disimpan dalam wadah.


 function useCount() { return useState(0) } let Count = createContainer(useCount) function useCounter() { let [count, setCount] = Count.useContainer() let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) let reset = () => setCount(0) return { count, decrement, increment, reset } } 

Kiat # 3: Optimalisasi Komponen


Tidak ada "optimasi" terpisah untuk yang unstated-next , metode yang biasa untuk mengoptimalkan komponen Bereaksi sudah cukup.


1) Optimalisasi subtree berat dengan memisahkan komponen.


Kepada:


 function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> <div> <div> <div> <div>   </div> </div> </div> </div> </div> ) } 

Setelah:


 function ExpensiveComponent() { return ( <div> <div> <div> <div>   </div> </div> </div> </div> ) } function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> <ExpensiveComponent /> </div> ) } 

2) Optimalisasi operasi berat dengan hook useMemo ()


Kepada:


 function CounterDisplay(props) { let counter = Counter.useContainer() //    ,   `counter` —   let expensiveValue = expensiveComputation(props.input) return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } 

Setelah:


 function CounterDisplay(props) { let counter = Counter.useContainer() //    ,     let expensiveValue = useMemo(() => { return expensiveComputation(props.input) }, [props.input]) return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } 

3) Kurangi jumlah rendering ulang menggunakan React.memo () dan useCallback ()


Kepada:


 function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay(props) { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } 

Setelah:


 function useCounter() { let [count, setCount] = useState(0) let decrement = useCallback(() => setCount(count - 1), [count]) let increment = useCallback(() => setCount(count + 1), [count]) return { count, decrement, increment } } let Counter = createContainer(useCounter) let CounterDisplayInner = React.memo(props => { return ( <div> <button onClick={props.decrement}>-</button> <p>You clicked {props.count} times</p> <button onClick={props.increment}>+</button> </div> ) }) function CounterDisplay(props) { let counter = Counter.useContainer() return <CounterDisplayInner {...counter} /> } 

Migrasi dengan unstated


Saya sengaja menerbitkan perpustakaan ini sebagai paket terpisah karena keseluruhan API benar-benar baru. Oleh karena itu, Anda dapat menginstal kedua paket secara paralel dan bermigrasi secara bertahap.


Bagikan kesan Anda tentang transisi ke yang unstated-next , karena selama beberapa bulan ke depan saya berencana untuk melakukan dua hal berdasarkan informasi ini:


  • Pastikan unstated-next memenuhi semua kebutuhan pengguna yang unstated .
  • Pastikan bahwa untuk yang unstated dinyatakan ada proses migrasi yang jelas dan singkat ke yang unstated-next .

Mungkin saya akan menambahkan beberapa API ke perpustakaan lama atau baru untuk membuat hidup lebih mudah bagi pengembang. Adapun unstated-next , saya berjanji bahwa API yang ditambahkan akan seminimal mungkin, dan saya akan melakukan yang terbaik untuk menjaga perpustakaan tetap kecil.


Di masa depan, saya mungkin akan unstated-next porting kode unstated-next kembali ke unstated sebagai versi utama baru. unstated-next masih akan tersedia sehingga Anda dapat menggunakan unstated@2 dan unstated-next dalam proyek yang sama secara paralel. Kemudian, ketika Anda menyelesaikan migrasi, Anda dapat meningkatkan ke unstated@3 dan menghapus unstated-next (tentu saja, memperbarui semua impor ... harus ada cukup pencarian dan ganti).


Meskipun ada perubahan dramatis dalam API, saya harap saya dapat memberi Anda migrasi semudah mungkin. Saya akan senang setiap komentar tentang apa yang bisa dilakukan dengan lebih baik.


Referensi


Source: https://habr.com/ru/post/id451410/


All Articles