最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to share React context state across separately mounted component trees - Stack Overflow

programmeradmin2浏览0评论

I understand React Context is useful for sharing state across a deep ponent tree without having to do prop drilling. However, I can't find a good way to share the same context values across multiple separate ponent trees. The app I work in isn't a true React front-end; rather, it has a few React trees sprinkled throughout the page. I'd like to share state among them using context. I know Redux can handle this, but I'm trying to avoid using that if possible.

// counter-context.jsx
import React, { useState, createContext } from 'react';

const CounterContext = createContext();

const CounterContextProvider = ({ children }) => {
  const [counter, setCounter] = useState(0);

  function increment() {
    setCounter(counter + 1);
  }

  return (
    <CounterContext.Provider value={{ counter, increment }}>
      {children}
    </CounterContext.Provider>
  );
};

export { CounterContextProvider, CounterContext };

// FirstComponent.jsx
import React, { useContext } from 'react';
import { render } from 'react-dom'; 

import { CounterContextProvider, CounterContext } from './CounterContext';

const FirstComponent = () => {
  const { counter } = useContext(CounterContext);

  return <p>First ponent: {counter}</p>;
};

render(
  <CounterContextProvider>
    <FirstComponent />
  </CounterContextProvider>,
  document.getElementById('first-ponent')
);

// SecondComponent.jsx
import React, { useContext } from 'react';
import { render } from 'react-dom';

import CounterContext from './CounterContext';

const SecondComponent = () => {
  const { counter } = useContext(CounterContext);

  return <p>Second ponent: {counter}</p>;
};

render(
  <CounterContextProvider>
    <SecondComponent />
  </CounterContextProvider>,
  document.getElementById('second-ponent')
);

If I add a button in FirstComponent that invokes increment, I want the updated counter value to also reflect in SecondComponent. But given a new instance of CounterContextProvider is created, I'm not sure how to acplish that. Is there a relatively clean way, or am I stuck with Redux?

I understand React Context is useful for sharing state across a deep ponent tree without having to do prop drilling. However, I can't find a good way to share the same context values across multiple separate ponent trees. The app I work in isn't a true React front-end; rather, it has a few React trees sprinkled throughout the page. I'd like to share state among them using context. I know Redux can handle this, but I'm trying to avoid using that if possible.

// counter-context.jsx
import React, { useState, createContext } from 'react';

const CounterContext = createContext();

const CounterContextProvider = ({ children }) => {
  const [counter, setCounter] = useState(0);

  function increment() {
    setCounter(counter + 1);
  }

  return (
    <CounterContext.Provider value={{ counter, increment }}>
      {children}
    </CounterContext.Provider>
  );
};

export { CounterContextProvider, CounterContext };

// FirstComponent.jsx
import React, { useContext } from 'react';
import { render } from 'react-dom'; 

import { CounterContextProvider, CounterContext } from './CounterContext';

const FirstComponent = () => {
  const { counter } = useContext(CounterContext);

  return <p>First ponent: {counter}</p>;
};

render(
  <CounterContextProvider>
    <FirstComponent />
  </CounterContextProvider>,
  document.getElementById('first-ponent')
);

// SecondComponent.jsx
import React, { useContext } from 'react';
import { render } from 'react-dom';

import CounterContext from './CounterContext';

const SecondComponent = () => {
  const { counter } = useContext(CounterContext);

  return <p>Second ponent: {counter}</p>;
};

render(
  <CounterContextProvider>
    <SecondComponent />
  </CounterContextProvider>,
  document.getElementById('second-ponent')
);

If I add a button in FirstComponent that invokes increment, I want the updated counter value to also reflect in SecondComponent. But given a new instance of CounterContextProvider is created, I'm not sure how to acplish that. Is there a relatively clean way, or am I stuck with Redux?

Share Improve this question edited Sep 9, 2019 at 19:56 Andrew Gibson asked Sep 9, 2019 at 19:32 Andrew GibsonAndrew Gibson 4417 silver badges17 bronze badges 3
  • You can create a context for the mon parent of those two ponents and pass only the info you need. – Praneeth Paruchuri Commented Sep 9, 2019 at 20:05
  • 3 The two ponents don't have a mon parent – Andrew Gibson Commented Sep 9, 2019 at 20:20
  • Can you clarify how redux could solve this problem? I am in a similar situation but I do have redux available to me. Thanks in advance! – austinbruch Commented Sep 3, 2020 at 14:46
Add a ment  | 

3 Answers 3

Reset to default 1

The two ponents can be rendered using react Portal so that they both fall under the same Parent.Now there will be one provider that can be used by both the ponents

As Hari Abinesh mentioned, portals is a way to solve this problem, if your use case is not too plex.

Docs: https://reactjs/docs/portals.html

// counter-context.jsx
import React, { useState, createContext } from 'react';

const CounterContext = createContext();

const CounterContextProvider = ({ children }) => {
  const [counter, setCounter] = useState(0);

  function increment() {
    setCounter(counter + 1);
  }

  return (
    <CounterContext.Provider value={{ counter, increment }}>
      {children}
    </CounterContext.Provider>
  );
};

export { CounterContextProvider, CounterContext };

// FirstComponent.jsx
import React, { useContext } from 'react';
import { render, createPortal } from 'react-dom';
import { CounterContextProvider, CounterContext } from './CounterContext';

const FirstComponent = () => {
  const { counter } = useContext(CounterContext);
  const portalElement = document.getElementById('span-portal')
  return (
    <React.Fragment>
      <p>First ponent: {counter}</p>
      {createPortal(counter, portalElement)}
    </React.Fragment>
  );
};

render(
  <CounterContextProvider>
    <FirstComponent />
  </CounterContextProvider>,
  document.getElementById('first-ponent')
);

// SecondComponent.jsx
import React from 'react';
import { render } from 'react-dom';

const SecondComponent = () => {
  return <p>Second ponent: <span id='span-portal'></span> </p>;
};

// At this point, you might not even need context at all if it's only going
// to be rendered from one ponent.
render(
  <SecondComponent />,
  document.getElementById('second-ponent')
);

Another way is to use react-relink. It is much simpler than Redux, designed to work across different React ponent trees and you don't need to wrap your ponents in any <Provider>s. That is, if you're willing to resort to more than just built-in React solutions.

// counter-context.jsx -> counter-source.jsx
import { createSource, useRelinkValue } from 'react-relink';

export const CounterSource = createSource({
  default: 1,
})

export function useCounterValue() {
  return useRelinkValue(CounterSource);
}

export function incrementCounter() {
  CounterSource.set(c => c + 1)
}

// FirstComponent.jsx
import React from 'react';
import { render } from 'react-dom';
import { useCounterValue } from './counter-source';

const FirstComponent = () => {
  const counter = useCounterValue();
  return <p>First ponent: {counter}</p>;
};

render(
  <FirstComponent />,
  document.getElementById('first-ponent')
);

// SecondComponent.jsx
import React from 'react';
import { render } from 'react-dom';
import { useCounterValue } from './counter-source';

const SecondComponent = () => {
  const counter = useCounterValue();
  return <p>Second ponent: {counter}</p>;
};

render(
  <SecondComponent />,
  document.getElementById('second-ponent')
);

You cannot share the context between two separate states. Unless you will be duplicating your context on the window object and reading from it. Also, there are these package which might help, however, this is very unadvisable: https://www.npmjs./package/@fluentui/global-context, https://github./dai-shi/use-context-selector#bridgeprovider

发布评论

评论列表(0)

  1. 暂无评论