【问题标题】:Creating Plugins in React在 React 中创建插件
【发布时间】:2022-06-19 02:22:07
【问题描述】:

我正在学习 React。我有使用 Vue.js 的经验。 Vue.js 具有plugins 的概念,它允许您在应用程序的各个部分 注入功能。这些部分可能是组件、状态管理、路由器等。对插件的常见需求是翻译或日志记录。我的问题是,React 有插件或服务之类的概念吗?如果有,是什么?

我在React docs 中没有看到任何类似于插件的东西。我看过的几篇博客文章似乎没有以同样的方式使用插件。如何提供可在整个 React 应用程序中全局使用的可编程访问功能?

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    一个常见的模式是为此使用React context。与您链接的Vue.js plugin documentation 非常相似的示例是这样的:

    const I18n = React.createContext();
    
    function useTranslate() {
      const i18n = React.useContext(I18n);
      
      return React.useCallback((key) => {
        return key.split('.').reduce((o, i) => {
          if (o) return o[i];
        }, i18n);
      }, [i18n]);
    }
    
    function App() {
      const translate = useTranslate();
    
      return (
        <h1>{translate("greetings.hello")}</h1>
      );
    }
    
    // app initialization
    const FR = {
      greetings: {
        hello: 'Bonjour!'
      }
    };
    
    const root = ReactDOM.createRoot(document.querySelector("#root"));
    root.render(
      <I18n.Provider value={FR}>
        <App />
      </I18n.Provider>
    );
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <div id="root"></div>

    以上内容相当有限,因为用户无法选择自己的语言。一个更复杂的例子是用户可以选择他们的语言。以下是有关如何实现此功能的一些灵感:

    // Create a component that wraps it't children in a context.
    function I18n({ initial, dicts, children }) {
      const [lang, setLang] = React.useState(initial);
      const dict = dicts[lang];
      
      const contextValue = React.useMemo(
        () => ({ lang, setLang, dict, dicts }),
        [lang, setLang, dict, dicts]
      );
    
      return (
        <I18n.Context.Provider
          value={contextValue}
          children={children}
        />
      );
    }
    
    // Create the actual React context.
    I18n.Context = React.createContext();
    
    // Provide custom hooks to simplify working with the data.
    // You could for example use a simple templating engine.
    I18n.useTranslate = function () {
      const { dict } = React.useContext(I18n.Context);
      
      return React.useCallback((key, view) => {
        const template = key.split(".").reduce((dict, key) => dict[key], dict);
        return Mustache.render(template, view);
      }, [dict]);
    };
    
    // Provide custom components to allow a user to interact with your context.
    I18n.LangSelect = function (props) {
      const { lang, setLang, dicts } = React.useContext(I18n.Context);
      
      const changeLang = React.useCallback((event) => {
        setLang(event.target.value);
      }, [setLang]);
    
      return (
        <select {...props} value={lang} onChange={changeLang}>
          {Object.entries(dicts).map(([key, dict]) => (
            <option key={key} value={key}>
              {dict.langSelect.label}
            </option>
          ))}
        </select>
      );
    };
    
    // All the above could be located in a `plugin/i18n.jsx` file, or
    // wherever you would like to store it. The code below would be
    // the usage of this "plugin".
    
    function App() {
      // Wrap the parts of the app you want to apply the context to.
      return (
        <div>
          <I18n.LangSelect />
          <Greet />
        </div>
      );
    }
    
    // Then in a child component use eiter `useContext` or the custom hook
    // we created earlier.
    function Greet() {
      const i18n = React.useContext(I18n.Context); // access the raw context data
      const translate = I18n.useTranslate(); // or use your custom hook(s)
    
      return (
        <h1>
          {i18n.dict.greetings.hello}{" "}
          {translate("greetings.introduce", { name: "John Doe" })}
        </h1>
      );
    }
    
    // app initialization
    
    // The dictionaries could be imported from a separate file.
    const DICTS = {
      EN: {
        langSelect: {
          label: "English",
        },
        greetings: {
          hello: "Hello!",
          introduce: "I'm {{name}}.",
        },
      },
      FR: {
        langSelect: {
          label: "Français",
        },
        greetings: {
          hello: 'Bonjour!',
          introduce: "Je m'appelle {{name}}.",
        },
      }
    };
    
    const root = ReactDOM.createRoot(document.querySelector("#root"))
    root.render(
      <I18n initial="EN" dicts={DICTS}>
        <App />
      </I18n>
    );
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <script crossorigin src="https://unpkg.com/mustache@4/mustache.js"></script>
    <div id="root"></div>

    请注意,您不一定需要 React 上下文。如果你有一个库函数或类似的东西,你可以将它导入到你的文件中并使用它。

    【讨论】:

      猜你喜欢
      • 2021-03-15
      • 2021-12-15
      • 2018-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-30
      相关资源
      最近更新 更多