我了解React Context对于在深层组件树中共享状态而不必进行prop钻探非常有用。但是,我找不到在多个单独的组件树之间共享相同上下文值的好方法。我使用的应用不是真正的React前端;相反,它在页面上散布着一些React树。我想使用上下文在它们之间共享状态。我知道Redux可以解决这个问题,但我尽量避免使用它。
// 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 component: {counter}</p>;
};
render(
<CounterContextProvider>
<FirstComponent />
</CounterContextProvider>,
document.getElementById('first-component')
);
// 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 component: {counter}</p>;
};
render(
<CounterContextProvider>
<SecondComponent />
</CounterContextProvider>,
document.getElementById('second-component')
);
如果我在FirstComponent
中添加一个调用increment
的按钮,我希望更新后的counter
值也能反映在SecondComponent
中。但是,鉴于创建了CounterContextProvider
的新实例,我不确定如何实现。有没有相对干净的方法,还是我坚持使用Redux?
答案 0 :(得分:0)
这两个组件可以使用react Portal呈现,因此它们都属于同一父对象。现在,将有一个提供程序可供两个组件使用
答案 1 :(得分:0)
正如 Hari Abinesh 提到的,如果您的用例不是太复杂,门户是解决这个问题的一种方法。
文档:https://reactjs.org/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 component: {counter}</p>
{createPortal(counter, portalElement)}
</React.Fragment>
);
};
render(
<CounterContextProvider>
<FirstComponent />
</CounterContextProvider>,
document.getElementById('first-component')
);
// SecondComponent.jsx
import React from 'react';
import { render } from 'react-dom';
const SecondComponent = () => {
return <p>Second component: <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 component.
render(
<SecondComponent />,
document.getElementById('second-component')
);
另一种方法是使用 react-relink。它比 Redux 简单得多,旨在跨不同的 React 组件树工作,您无需将组件包装在任何 <Provider>
中。也就是说,如果您愿意使用的不仅仅是内置的 React 解决方案。
// 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 component: {counter}</p>;
};
render(
<FirstComponent />,
document.getElementById('first-component')
);
// SecondComponent.jsx
import React from 'react';
import { render } from 'react-dom';
import { useCounterValue } from './counter-source';
const SecondComponent = () => {
const counter = useCounterValue();
return <p>Second component: {counter}</p>;
};
render(
<SecondComponent />,
document.getElementById('second-component')
);