Проблема:

Работая с большими проектами React в монорепозиториях, наша команда начала бороться с количеством провайдеров, которые начали появляться как грибы после дождя. Провайдеры не плохие. Они являются решением общей проблемы «сверления подпорок». Поэтому началось строительство космического корабля.

<I18nProvider
    production={environment.production}
>
    <DataProvider data={props.data}>
        <ActiveDialogProvider>
            <PublicFetchProvider>
                <AuthProvider>
                    <PrivateFetchProvider>
                        <AuthFetchProvider>
                            <CustomThemeProvider>
                                <CustomMuiPickersUtilsProvider>
                                    <LegalsProvider>
                                        <PaymentMethodsProvider>
                                            <CartProvider>
                                                {props.children}
                                            </CartProvider>
                                        </PaymentMethodsProvider>
                                    </LegalsProvider>
                                </CustomMuiPickersUtilsProvider>
                            </CustomThemeProvider>
                        </AuthFetchProvider>
                    </PrivateFetchProvider>
                </AuthProvider>
            </PublicFetchProvider>
        </ActiveDialogProvider>
    </DataProvider>
</I18nProvider>

В какой-то момент кончик космического корабля стал не помещаться на экране, не говоря уже об ограничении строки в 80 символов. Поэтому мы начали реорганизовывать это, группируя провайдеров в функциональные компоненты, которые также использовались в качестве провайдеров. Такой подход помог нам уменьшить The Spaceship. Но взамен мы получили проблемы посерьезнее:
* Порядок зависимостей, когда провайдер группы сверху зависел от провайдера из группы снизу;
* Увеличенный объем кода, что было вызвано размножением файлов;
* Потеря полноты по всему проекту.

Решение:

Решения, найденные в дикой природе, не были хорошими. Некоторые из них устарели, некоторые несовместимы или отсутствуют типы. Итак, я решил сделать тот, который соответствует всем моим требованиям.

В качестве основы я взял подход к снабжению моего провайдера массивом и использованию reduce() для вложения провайдеров. Но вскоре выяснилось, что это создало новую проблему, загромождая файл функциями Карри. Эти функции использовались для инкапсуляции провайдеров, которым требовались некоторые внешние параметры, такие как Redux-Toolkit с хранилищем <Provider store={store}>. Это приводит меня к использованию шаблона flatMap в массиве. Этот массив может иметь пары поставщиков и их свойства, заключенные в другой массив.

<FlatProviders
    providers={[
        [I18nProvider, { production: environment.production }],
        [DataProvider, { data: props.data }],
        ActiveDialogProvider,
        PublicFetchProvider,
        AuthProvider,
        PrivateFetchProvider,
        AuthFetchProvider,
        CustomThemeProvider,
        CustomMuiPickersUtilsProvider,
        LegalsProvider,
        PaymentMethodsProvider,
        CartProvider,
    ]}
>
    {props.children}
</FlatProviders>

В качестве бонуса я обнаружил, что могу кормить FlatProviders даже Context.Providers непосредственно из React.createContext(). Хотя не совсем уверен, что это хорошая идея, функция есть.

Это решение было принято и успешно использовано командой.
Надеюсь, оно поможет вам и вашей команде.

Код можно найти здесь:

https://github.com/sincovschi/react-flat-providers
Взгляните на папку /example, чтобы узнать, как она используется.

Пусть клопы обходят вас стороной,
Евгений.