1

我想要的是:

我正在尝试将动态主题选项添加到我正在处理的react-styleguidist项目中。按照这个未完成且已关闭的 pr中提出的想法,我添加了一个自定义ThemeSwitcher组件,它是一个呈现在目录侧边栏中的选择菜单。选择一个选项应该更新brand上下文,它使用 styled-components' 呈现相应的主题BrandProvider。它应该像封闭的 pr 中包含的演示一样运行:https ://fancy-sg.surge.sh/ 。

什么不起作用:

我无法访问我在和中提供和更新的相同ThemedWrapper上下文。检查 React Components 控制台中的树,看起来 react-styleguidist 可能会.StyleguideWrapperThemeSwitcherReactExample StyleguideRenderer

假设我对由于上下文ThemedWrapper位于外部而没有更新的上下文是正确的StyleGuideRenderer,我有两个高级想法(但无法弄清楚如何去做)是:

  1. 在 react-styleguidist 库中找到作为两者的祖先的正确组件,StyleGuideRenderer 在那里添加,以便现在可以访问上下文ReactExampleBrandProviderThemedWrapper
  2. 我还没有找到的其他一些上下文配置将允许两个组件在没有提供者作为祖先的情况下使用相同的上下文(这可能吗??)

我有的:

这是我正在使用的相关代码的精简版本。

brand-context.js(导出上下文和提供者,受Kent C Dodds启发

import React, { createContext, useState, useContext } from 'react';

const BrandStateContext = createContext();
const BrandSetContext = createContext();

function BrandProvider({ children, theme }) {
  const [brand, setBrand] = useState(theme);
  return (
    <BrandStateContext.Provider value={brand}>
      <BrandSetContext.Provider value={(val) => setBrand(val)}>
        {children}
      </BrandSetContext.Provider>
    </BrandStateContext.Provider>
  );
}

function useBrandState() {
  return useContext(BrandStateContext);
}

function useBrandSet() {
  return useContext(BrandSetContext);
}

export { BrandProvider, useBrandState, useBrandSet };

StyleGuideWrapper.jsx(rsg-components/StyleguideRenderer 的副本,添加了 ThemeSwitcher 组件以从 ui 切换主题;在 styleguide 配置中传递为StyleGuideRenderer

import React from 'react';
import cx from 'clsx';
import Styled from 'rsg-components/Styled';

import ThemeSwitcher from './ThemeSwitcher';
import { BrandProvider } from './brand-context';

export function StyleGuideRenderer({ children, classes, hasSidebar, toc }) {
  return (
    <BrandProvider>
      <div className={cx(classes.root, hasSidebar && classes.hasSidebar)}>
        <main className={classes.content}>
          {children}
        </main>
        {hasSidebar && (
          <div className={classes.sidebar} data-testid="sidebar">
            <section className={classes.sidebarSection}>
              <ThemeSwitcher classes={classes} />
            </section>
            {toc}
          </div>
        )}
      </div>
    </BrandProvider>
  );
}

StyleGuideRenderer.propTypes = propTypes;

export default Styled(styles)(StyleGuideRenderer);

ThemeSwitcher.jsx

import React from 'react';
import Styled from 'rsg-components/Styled';

import { useBrandSet, useBrandState } from './brand-context';

const ThemeSwitcher = ({ classes }) => {
  const brand = useBrandState();
  const setBrand = useBrandSet();
  const onBrandChange = (e) => setBrand(e.target.value);

  const brands = ['foo', 'bar'];

  return (
    <label className={classes.root}>
      Brand
      <select value={brand} onChange={onBrandChange}>
        {brands.map((brand) => (
          <option key={brand} value={brand}>{brand}</option>
        ))}
      </select>
    </label>
  );
};

export default Styled(styles)(ThemeSwitcher);

ThemedWrapper.jsx(在 styleguide 配置中作为 传递Wrapper,并包装每个示例组件以将它们提供给 styled-components)

import React from 'react';
import { ThemeProvider } from 'styled-components';
import { BrandStateContext } from './brand-context';

const LibraryProvider = ({ brand, children }) => {
  return (
    <ThemeProvider theme={brand}>{children}</ThemeProvider>
  );
};

function ThemedWrapper({ children }) {
  return (
    <BrandStateContext.Consumer>
      {brand => (
        <LibraryProvider brand={brand}>{children}</LibraryProvider>
      )}
    </BrandStateContext.Consumer>
  );
}

export default ThemedWrapper;
4

0 回答 0