Saya memiliki banyak konteks dan saya harus menulis dengan cara ini, sangat jelek! Itu menghalangi pekerjaan saya sekarang. Desain seperti itu membuatnya hampir tidak bisa digunakan.
<context1.Provider value={value1}>
<context2.Provider value={value2}>
<context3.Provider value={value3}>
<context4.Provider value={value4}>
<context5.Provider value={value5}>
</context5.Provider>
</context4.Provider>
</context3.Provider>
</context2.Provider>
</context1.Provider>
<context1.Consumer>
{value1 => <context2.Consumer>
{value2 => <context3.Consumer>
{value3 => <context4.Consumer>
{value4 => <context5.Consumer>
{value5 => (
null
)}
</context5.Consumer>}
</context4.Consumer>}
</context3.Consumer>}
</context2.Consumer>}
</context1.Consumer>
API Hooks yang akan datang menyediakan cara berbeda untuk menggunakan konteks.
Terima kasih. Tapi bagaimana dengan provider?
Saya akan jujur. Jika Anda menghadapi implementasi semacam ini, maka desain arsitektur Anda tampak buruk dan Anda mungkin tidak boleh menggunakan konteks React.
Tidak, saya sedang mendesain wadah toko baru. Ini perlu bekerja dengan konteks dalam bereaksi.
https://github.com/rabbitooops/rako
Apa hubungan perpustakaan itu dengan 5 lapisan penyedia / konsumen?
Karena itu mendukung injeksi banyak toko untuk bereaksi daripada solusi toko tunggal seperti Redux.
Dalam hal ini, penanganan konteks sekarang ada pada pengguna perpustakaan, dan lebih sedikit pada perpustakaan. Bagaimana mereka memanfaatkan fitur-fiturnya terserah mereka, dan jika mereka menginginkan semua penyedia di satu tempat (yang mengalahkan tujuan memiliki banyak toko), maka itulah pilihan mereka. Idealnya, solusi beberapa toko akan diterapkan pada pemisahan yang berbeda dalam aplikasi, jadi konteks bersarang seperti ini jauh lebih jarang.
Saya setidaknya 2 sen.
Jadi API konteks hanya ramah untuk solusi penyimpanan tunggal seperti redux.
Tidak semuanya. Tetapi juga sulit untuk mendiskusikan masalah Anda tanpa contoh yang lebih realistis. Silakan buat satu?
Bayangkan ada tiga tema toko, pengguna dan penghitung.
function theme(getState) {
return {
color: 'white',
setColor(color) {
this.setState({color})
}
}
}
function user(getState) {
return {
name: '',
setName(name) {
this.setState({name})
}
}
}
function counter(getState) {
return {
value: 0,
increment() {
const {value} = getState()
this.setState({value: value + 1})
}
}
}
const [themeStore, userStore, counterStore] = createStores(theme, user, counter)
const [themeContext, userContext, counterContext] = createContexts(themeStore, userStore, counterStore)
class App extends React.Component {
render() {
return (
<themeContext.StoreProvider>
<userContext.StoreProvider>
<counterContext.StoreProvider>
<Child />
</counterContext.StoreProvider>
</userContext.StoreProvider>
</themeContext.StoreProvider>
)
}
}
class Child extends React.Component {
static contextType = [themeContext, userContext]
render() {
const [theme, user] = this.context
/* ... */
}
}
Apa sintaks ideal Anda?
Gunakan context.write, dukung penggunaan banyak konteks tanpa bersarang.
Mengkonsumsi banyak konteks tanpa bersarang sudah didukung. (Dengan Hooks.)
Context.write memiliki RFC yang terbuka untuk itu. Kami tidak tahu apakah itu akan berhasil karena ini menimbulkan beberapa pertanyaan yang sangat rumit. Tetapi saat RFC terbuka, saya tidak yakin apa yang dapat ditindaklanjuti dalam masalah ini. Apakah Anda memiliki sesuatu untuk ditambahkan selain apa yang sudah ada dalam motivasi RFC?
Saya ingin mengajukan pertanyaan. Mengapa tidak bereaksi mendukung mengkonsumsi banyak konteks di kelas? Sepertinya API hooks memiliki banyak masalah yang harus dipecahkan dan saat ini sangat tidak stabil.😨🧐😥🤕
static contextType = [themeContext, userContext]
const [theme, user] = this.context
Saya akan menerapkan StoreProviders
yang dapat menyarangkan banyak konteks secara otomatis.
const StoreProviders = constructStoreProviders(...storeContexts)
<StoreProviders>
<Child />
</StoreProviders>
Terima kasih atas bantuan Anda Dan! :)
@rabbitooops Masalah apa sebenarnya yang terkait dengan pengait yang Anda miliki? Saya menggunakan pengait dalam produksi dan mereka bekerja dengan baik untuk tim saya.
Dan bagaimana dengan ini? Aman menggunakan kail sekarang. @gaul
// A library
function useStoreProviders(Component, ...contexts) {
contexts.forEach(context => Component.useProvider(context, someValue))
}
// User code
function App(props) {
const [theme] = useState('white')
// Safe to use `component hook`.
App.useProvider(themeContext, theme)
App.useShouldComponentUpdate(() => {})
// Meanwhile, library can also use `component hook`.
useStoreProviders(App, storeContext1, storeContext2, storeContext3)
// Normal hook can't use `component hook`.
customHook()
/* ... */
}
<App />
decorateBeforeRender(App)
@rabbitooops https://github.com/reactjs/rfcs/issues/101
@rabbitooops Bagaimana jika menggunakan satu penyimpanan dan Simbol sebagai kunci untuk meniru multi-lapisan penyimpanan?
data Store = Leaf Object | C Store Store
Atau dengan cara yang tidak sempurna di javascript:
const LEFT = Symbol('LEFT')
const RIGHT = Symbol('RIGHT')
function createLeafStore = return new Store({});
function createStore(leftChild :: Store, rightChild :: Store) {
return new Store({[LEFT]: leftChild, [Right]: rightChild})
}
@TrySound Berbeda: App.useProvider
@zhujinxuan Maaf, saya tidak bisa mendapatkan Anda.
@zhujinxuan Anda dapat menggunakan Unstated , contoh:
<Subscribe to={[AppContainer, CounterContainer, ...]}>
{(app, counter, ...) => (
<Child />
)}
</Subscribe>
Sepertinya hooks API memiliki banyak masalah yang harus diselesaikan dan saat ini sangat tidak stabil
Kami sedang mempersiapkannya untuk rilis dalam satu atau dua minggu - tidak yakin mengapa Anda menyimpulkannya. Mereka akan segera siap meskipun jika Anda ingin aman, harap tunggu sampai rilis stabil.
Dan bagaimana dengan ini?
Memanggil Hooks dalam satu putaran (seperti yang Anda lakukan di forEach
) umumnya tidak diperbolehkan. Cara ini mudah menyebabkan masalah.
useStoreProviders
Baik useProvider
dan useShouldComponentUpdate
bermasalah karena Hooks (itulah sebabnya React tidak memilikinya). Lihat tanggapan saya di https://github.com/facebook/react/issues/14534#issuecomment -455411307.
Secara keseluruhan, saya berjuang untuk memahami maksud dari masalah ini.
Mengkonsumsi banyak konteks diselesaikan dengan useContext
Hook. Kami tidak menyarankan untuk "mengotomatiskan" dengan array karena ini membuatnya terlalu mudah untuk menulis komponen yang berlangganan terlalu banyak konteks dan merender ulang terlalu sering. Anda harus memiliki pemahaman yang jelas tentang konteks mana yang didengarkan komponen, yang diberikan oleh useContext
API kepada Anda. Jika harus, Anda dapat menulis useMyContexts()
Hook yang secara eksplisit menggunakan konteks tertentu. Saya hanya tidak merekomendasikan membuatnya dinamis seperti yang Anda lakukan karena jika panjang array berubah, itu bisa rusak.
Menempatkan beberapa penyedia dapat dilihat sebagai "boilerplate" dan pada akhirnya kami mungkin memiliki solusi untuk ini. Tapi saya juga tidak mengerti mengapa Anda melihatnya sebagai masalah besar. Contoh di utas ini tidak cukup realistis untuk menjelaskan masalahnya kepada saya. Saya tidak melihat sesuatu yang buruk dengan menempatkan beberapa lapisan JSX di suatu tempat di bagian atas aplikasi. Cukup yakin Anda memiliki lebih dalam div
bersarang di sebagian besar komponen dan itu tidak terlalu menyakitkan.
Saya akan menutup ini karena saya pikir saya sudah menjawab poin-poin ini, dan diskusi berjalan berputar-putar. Jika ada sesuatu yang hilang beri tahu saya.
OT: @gaearon , apakah ada rencana untuk menambahkan sesuatu seperti useRender
atau sesuatu untuk lebih mengontrol rendering? misalnya:
useRender(() => <div />, [...props])
Argumen kedua memiliki peran yang sama yaitu useEffect
hook.
useMemo
adalah temanmu.
Lihat cuplikan kedua di https://reactjs.org/docs/hooks-faq.html#how -to-memoize-calculator.
Saya berakhir dengan kode seperti itu:
function provider<T>(theProvider: React.Provider<T>, value: T) {
return {
provider: theProvider,
value
};
}
function MultiProvider(props: {providers: Array<{provider: any; value: any}>; children: React.ReactElement}) {
let previous = props.children;
for (let i = props.providers.length - 1; i >= 0; i--) {
previous = React.createElement(props.providers[i].provider, {value: props.providers[i].value}, previous);
}
return previous;
}
Kemudian di komponen penyediaan tingkat atas saya:
public render() {
return (
<MultiProvider
providers={[
provider(Context1.Provider, this.context1),
provider(Context2.Provider, this.context2),
provider(Context3.Provider, this.context3),
provider(Context4.Provider, this.context4),
provider(Context5.Provider, this.context5),
]}
><AppComponents />
</MultiProvider>
}
@gaul
Saya tidak melihat sesuatu yang buruk dengan menempatkan beberapa lapisan JSX di suatu tempat di bagian atas aplikasi.
Saya memiliki ~ 15 ketergantungan yang ingin saya suntikkan dengan cara itu, dan memiliki 15 tingkat lekukan tidak terlihat cantik bagi saya :)
@ 0xorial Anda tidak benar-benar perlu memiliki komponen untuk itu, karena <>> hanyalah sebuah pemanggilan fungsi React.createElement. Jadi Anda bisa menyederhanakannya menjadi fungsi tulis seperti:
const compose = (contexts, children) =>
contexts.reduce((acc, [Context, value]) => {
return <Context.Provider value={value}>{acc}</Context.Provider>;
}, children);
dan gunakan sebagai:
import Context1 from './context1';
import Context2 from './context2';
import Context3 from './context3';
...
import Context15 from './context15';
const MyComponent = (props) => {
// const value1..15 = ... get the values from somewhere ;
return compose(
[
[Context1, value1],
[Context2, value2],
[Context3, value3],
...
[Context15, value15],
],
<SomeSubComponent/>
);
}
Saya pernah menulis perpustakaan yang menangani kasus ini: https://github.com/disjukr/join-react-context
ini pasti sesuatu yang terjadi di aplikasi sepanjang waktu. useContext
bagus untuk _mengonsumsi_ data kontekstual dalam sebuah komponen, tetapi tidak terlalu bagus ketika Anda perlu _provide_ konteks dalam aplikasi dengan banyak penyedia.
Berikut adalah alternatif penutupan dari solusi @alesmenzelsocialbakers :
const composeProviders = (...Providers) => (Child) => (props) => (
Providers.reduce((acc, Provider) => (
<Provider>
{acc}
</Provider>
), <Child {...props} />)
)
const WrappedApp = composeProviders(
ProgressProvider,
IntentsProvider,
EntitiesProvider,
MessagesProvider
)(App)
ReactDOM.render(<WrappedApp />, document.getElementById('root'));
Kelemahannya adalah Anda harus menulis setiap komponen Penyedia tertentu.
Contoh:
export const ProgressProvider = ({ children }) => {
const [progress, setProgress] = useState(0)
return (
<ProgressContext.Provider value={{ progress, setProgress }}>
{children}
</ProgressContext.Provider>
)
}
Saya telah membuat perpustakaan manajemen negara bagian yang lebih baik dalam komposisi layanan. Berikut adalah demo menghindari neraka penyedia . Jangan ragu untuk mencobanya atau membaca sumbernya (100 baris kode)!
Ini memperkenalkan objek "cakupan" untuk mengumpulkan penyedia konteks, sehingga:
Saya mengambil celah dalam hal ini juga. Ini sepertinya berfungsi dengan baik:
const composeWrappers = (
wrappers: React.FunctionComponent[]
): React.FunctionComponent => {
return wrappers.reduce((Acc, Current): React.FunctionComponent => {
return props => <Current><Acc {...props} /></Current>
});
}
Penggunaannya adalah:
const SuperProvider = composeWrappers([
props => <IntlProvider locale={locale} messages={messages} children={props.children} />,
props => <ApolloProvider client={client}>{props.children}</ApolloProvider>,
props => <FooContext.Provider value={foo}>{props.children}</FooContext.Provider>,
props => <BarContext.Provider value={bar}>{props.children}</BarContext.Provider>,
props => <BazContext.Provider value={baz}>{props.children}</BazContext.Provider>,
]);
return (
<SuperProvider>
<MainComponent />
</SuperProvider>
);
Saya juga menerbitkan helper ini sebagai perpustakaan npm react-compose-wrappers
Berikut ini menunjukkan bagaimana saya meneruskan pengguna yang diautentikasi ke komponen yang membutuhkannya.
Saya memutuskan untuk membuat satu status untuk aplikasi saya. Di file State.js saya, saya mengatur status awal, konteks, peredam, penyedia, dan hook.
import React, { createContext, useContext, useReducer } from 'react';
const INITIAL_STATE = {}
const Context = createContext();
const reducer = (state, action) =>
action
? ({ ...state, [action.type]: action[action.type] })
: state;
export const Provider = ({ children }) => (
<Context.Provider value={ useReducer(reducer, INITIAL_STATE) }>
{ children }
</Context.Provider>
);
const State = () => useContext(Context);
export default State;
Lalu di file index.js saya membungkus aplikasi saya di penyedia.
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from './State';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<Provider>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root'),
);
Untuk mengkonsumsi state dalam sebuah komponen saya bisa menggunakan hook. Saya juga dapat menggunakan pengiriman untuk memperbarui status. Misalnya jika saya ingin mendapatkan atau menetapkan pengguna.
import React, {useEffect} from 'react';
import State from './State'
const ExampleComponent = () => {
const [{ user }, dispatch] = State();
useEffect(() => {
const getUser = async () => {
const data = await fetch('http://example.com/user.json'); // However you get your data
dispatch({ type: 'user', user: data });
}
getUser();
}, [dispatch]);
// Don't render anything until user is retrieved
// The user is undefined since I passed an empty object as my initial state
if(user === undefined) return null;
return(
<p>{user.name}</p>
);
}
export default ExampleComponent;
Saya pikir cara ini memberi saya kebebasan untuk membangun keadaan seperti yang saya butuhkan tanpa menambahkan banyak konteks tambahan dan membantu saya menghindari sarang penyedia yang dalam.
API Hooks yang akan datang menyediakan cara berbeda untuk menggunakan konteks.
Bagaimana cara menggunakan ini di komponen kelas?
API Hooks yang akan datang menyediakan cara berbeda untuk menggunakan konteks.
https://reactjs.org/docs/hooks-reference.html#usecontextBagaimana cara menggunakan ini di komponen kelas?
Bukankah hook digunakan untuk memanfaatkan berbagai fitur React tanpa kelas menulis?
Artinya, semua yang dilakukan berbagai hook sudah ada di kelas. Jika Anda berbicara tentang sintaks yang mudah digunakan dan penggunaan api, maka react berpindah dari kelas ke komponen fungsional, jadi selamat datang di fungsi dan kait)
Saya membuat paket untuk memecahkan masalah dengan menyediakan API serupa dengan vue3
https://github.com/TotooriaHyperion/react-multi-provide
memperhatikan:
Outer.tsx
import React, { useMemo } from "react";
import { Providers, useCreateContexts, useProvide } from "../..";
import { createService, ServiceA } from "./service";
export const Outer: React.FC = ({ children }) => {
const contexts = useCreateContexts();
const service = useMemo(createService, []);
useProvide(contexts, ServiceA.id, service);
return <Providers contexts={contexts}>{children}</Providers>;
};
Inner2.tsx
import React from "react";
import { useContexts, useReplaySubject } from "../..";
import { ServiceA } from "./service";
export const Inner2: React.FC = () => {
const [
{
state$,
actions: { inc, dec },
},
] = useContexts([ServiceA.id]);
const count = useReplaySubject(state$);
return (
<>
<p>{count}</p>
<div>
<button onClick={inc}>Increment</button>
<button onClick={dec}>Decrement</button>
</div>
</>
);
};
inilah cara saya melakukannya:
interface Composable {
(node: React.ReactNode): React.ReactElement
}
const composable1: Composable = (node)=>{
return <someContext.Provider>{node}</someContext.Provider>
}
function Comp({children}:{children?:React.ReactNode}){
return pipe(
composabl1, composabl2, composable3
)(children)
}
Anda dapat menemukan fungsi pipe
di banyak pustaka populer seperti rxjs, ada juga beberapa proposal tingkat bahasa untuk operasi mirip pipeline ini. Tidak perlu 'menyelesaikan' dengan menggunakan lib lain.
Komentar yang paling membantu
@ 0xorial Anda tidak benar-benar perlu memiliki komponen untuk itu, karena <>> hanyalah sebuah pemanggilan fungsi React.createElement. Jadi Anda bisa menyederhanakannya menjadi fungsi tulis seperti:
dan gunakan sebagai: