简体   繁体   中英

Context API - multiple providers + React hooks for multiple pages?

How can simplify the React Context API providers without having to wrap my parent component with all those Providers? I have a couple of pages: Home, Blog, Contact, Gallery and About. I have created a Context API Context and Provider for the contact page. Every single Provider has its own unique API which I'm fetching using Axios with React Hooks.

What I am asking is there a way, to keep my code clean and dry not to repeat myself over and over again.

For instance, I would like to have one Context function, which I can use over and over again and only get the API data for one unique Axios API URL.

How can I do this? Copy pasting the same thing.. is not clean.

PageContext.js: 

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

import axios from 'axios';

import PropTypes from 'prop-types';

import { PAGE_CONTACT } from 'constants/import';

export const PageContext = createContext();

export const PageProvider = props => {
    const [contactPage, setContactPage] = useState([]);

    useEffect(() => {
        axios.get(PAGE_CONTACT).then(result => setContactPage(result.data));
    }, []);

    return (
        <PageContext.Provider value={[contactPage, setContactPage]}>
            {props.children}
        </PageContext.Provider>
    );
};

PageProvider.propTypes = {
    children: PropTypes.oneOfType([
        PropTypes.arrayOf(PropTypes.node),
        PropTypes.node,
    ]).isRequired,
};

Page Contact (component) where I use the Context:

import React, { useContext } from 'react';

import ReactHtmlParser from 'react-html-parser';

import { PageContext } from 'pages/context/PageContext';

const Contact = () => {
    const [field] = useContext(PageContext);

    return (
        <section className="contact">
            <div className="page">
                <div className="row">
                    <div className="col-xs-12">
                        <h3 className="section__title">{field.page_title}</h3>
                        {ReactHtmlParser(field.page_content)}
                        {field.page_featured_image && (
                            <img src={field.page_featured_image.path} />
                        )}
                    </div>
                </div>
            </div>
        </section>
    );
};

export default React.memo(Contact);

Index.js Wrapped the with the provider:

import React from 'react';
import ReactDOM from 'react-dom';

import { PageProvider } from 'pages/context/PageContext';

// Styles
import 'sass/root.scss';

// Root App
import App from './App';

import * as serviceWorker from './serviceWorker';

ReactDOM.render(
    <PageProvider>
        <App />
    </PageProvider>,
    document.getElementById('root')
);

serviceWorker.unregister();

It's an interesting approach, I personally would not use context for your solution.

My reasoning for this is that using a provider implies that you want this value prop to be accessible for every react children prop (which makes no sense for a contact page).

I can only assume that your contact page will have contents that only that pages requires? If so just doing some think like:

Contact Page:

import React, { useState, useEffect } from "react";

import axios from "axios";
import ReactHtmlParser from "react-html-parser";

const Contact = () => {
  const [pageData, setPageData] = useState();

  useEffect(() => {
    axios.get(PAGE_CONTACT).then(result => {
      // Axios - check success!
      if (result.meta === 200) {
        setPageData(result.data);
      }
    });
    // You should be doing error handling here as well!
  }, []);

  return (
    <section className="contact">
      <div className="page">
        <div className="row">
          <div className="col-xs-12">
            {!!pageData ? (
              <>
                <h3 className="section__title">{pageData.page_title}</h3>

                {ReactHtmlParser(pageData.page_content)}

                {pageData.page_featured_image && (
                  <img src={pageData.page_featured_image.path} />
                )}
              </>
            ) : (
              <p>Some loading UI...</p>
            )}
          </div>
        </div>
      </div>
    </section>
  );
};

export default Contact;

I would say the key take away here is to question you intent with react context. As adding this to the main component will trigger a re-render of the dom when the value changes.

You may have UX that is not dependant on contact page, eg an about page.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM