![](/img/trans.png)
[英]I can't access the Context Provider in useContext hook in React
[英]How can I use react useContext , to show data from context, in the same component
我正在創建一個反應向導組件,並希望使用上下文在父母和孩子之間傳遞數據
所以我創建了一個向導、上下文、提供程序和自定義掛鈎,但問題是如果我嘗試在向導組件上使用上下文,它不會顯示正確的信息
(見https://codesandbox.io/embed/wizardwitcontext-rfpui )
如何做到這一點,以便我可以依賴向導本身的上下文中的數據,以便我可以將登錄名轉移到自定義掛鈎?
使用向導.js:
import React, { useContext, useEffect } from "react";
import { WizardContext } from "./WizardContext";
const useWizard = () => {
const [state, setState] = useContext(WizardContext);
function setMaxSteps(maxSteps) {
setState(state => ({ ...state, maxSteps }));
}
function moveToStep(index) {
if (state.maxSteps && state.maxSteps > index) {
setState({ ...state, currentStep: index });
return index;
}
return state.currentStep;
}
function back() {
if (state.maxSteps) {
if (state.currentStep > 0) {
setState({ ...state, currentStep: state.currentStep - 1 });
window.scrollTo(0, 0);
}
}
}
//move back a step
function next() {
if (state.currentStep < state.maxSteps) {
setState({ ...state, currentStep: state.currentStep + 1 });
window.scrollTo(0, 0);
}
}
return {
setMaxSteps,
moveToStep,
back,
next,
maxSteps: state.maxSteps,
currentStep: state.currentStep,
state
};
};
export default useWizard;
向導.jsx:
const { state, currentStep, back, next, maxSteps, setMaxSteps } = useWizard();
return (
<div className="wizard">
<WizardProvider
maxSteps={React.Children.count(props.children)}
currentStep={0}
>
{/* <div className="wizard__upper">
<ProgressIndicator currentIndex={selected} onChange={onClick}>
{steps}
</ProgressIndicator>
<Button id="wizardCloseBtn" kind="ghost" onClick={onClose}>
<Icon icon={iconHeaderClose} />
</Button>
</div> */}
<div className="wizard__separator" />
<div className="wizard__content">
{`in wizard: cur=${currentStep}, max=${maxSteps}`}
{/* {getContentAt(0)} */}
{stepContentWithProps}
</div>
{/* <div className="wizard__buttons">
{showBack && (
<Link id="back" onClick={back}>
back
</Link>
)}
{showNext && (
<button id="next" onClick={next} kind="secondary">
Next Step
</button>
)}
</div> */}
</WizardProvider>
</div>
);
第2步:
import React, { useState, useContext, useEffect } from "react";
import useWizard from "./useWizard";
function Step2(props) {
const {
currentStep,
moveToStep,
maxSteps,
setMaxSteps,
next,
prev
} = useWizard();
return (
<div>
<p>Step 2</p>
{`in step2 (inner child of wizard): cur=${currentStep} see that cur !== cur from wizard above`}
<br />
<button onClick={() => moveToStep(1)}>
Click me to change current step
</button>
</div>
);
}
export default Step2;
最終結果是:
in wizard: cur=undefined, max=undefined
p1
in index.js: cur=undefined
Step 2
in step2 (inner child of wizard): cur=0 see that cur !== cur from wizard above
您在與Context.Provider
相同的級別調用useContext
:
function Wizard(props) {
// useWizard calls useContext
const { state, currentStep, back, next, maxSteps, setMaxSteps } = useWizard();
return (
<div className="wizard">
<WizardProvider
maxSteps={React.Children.count(props.children)}
currentStep={0}
>
<div className="wizard__content">
{`in wizard: cur=${currentStep}, max=${maxSteps}`}
</div>
</WizardProvider>
</div>
);
}
您需要更改結構並在Provider
子級中調用useContext
。
function Wizard(props) {
// useWizard calls useContext
const { state, currentStep, back, next, maxSteps, setMaxSteps } = useWizard();
return (
// v You trying to get Provider's value here
<div className="wizard">
<WizardProvider
maxSteps={React.Children.count(props.children)}
currentStep={0}
>
// v useContext available within the children
<ComponentA />
<ComponentB />
</WizardProvider>
</div>
);
}
參考上下文 API , useContext
。
上下文來自組件樹上方最近的提供者。 從反應文檔。
常量值 = useContext(MyContext);
接受上下文 object(從 React.createContext 返回的值)並返回該上下文的當前上下文值。 當前上下文值由樹中調用組件上方最近的值 prop 確定。
在這種情況下,您有 2 個選項。
1.您需要將您的應用程序(index.js)組件包裝在提供程序中。
或者
2.讓Wizard組件成為provider,並嘗試在子組件中使用useContext hook。
演示: https://stackblitz.com/edit/react-msac8q
希望這可以幫助
我認為由於我們不能在與 Provider 相同的組件中使用 useContext(),我認為我們可以做一些解決方法,我認為這將有助於在頁面/屏幕等主要組件中使用
// This will be your child component
function Wizard(props) {
// useWizard calls useContext
const { state, currentStep, back, next, maxSteps, setMaxSteps } = useWizard();
return (
<div className="wizard">
<div className="wizard__content">
{`in wizard: cur=${currentStep}, max=${maxSteps}`}
</div>
</div>
);
}
// This is your main Page
export default function WizardPage(){
return <WizardProvider
maxSteps={React.Children.count(props.children)}
currentStep={0}>
<Wizard />
</WizardProvider>
}
我找到了解決方案,感謝這篇文章: https://dev.to/email2vimalraj/react-hooks-lift-up--pass-down-state-using-usecontext-and-usereducer-5ai0描述的解決方案是在向導文件上創建一個 reducer,因此向導可以訪問其數據,也可以訪問子節點:
向導.jsx
import React, {
useState,
useEffect,
useLayoutEffect,
useContext,
useReducer
} from "react";
import PropTypes from "prop-types";
import "./wizard.scss";
import {
WizardContext,
wizardReducer,
SET_CURRENT_STEP,
SET_MAX_STEPS,
BACK,
NEXT
} from "./WizardContext";
function StepContent(props) {
const { selected, children, ...other } = props;
return (
<li {...other} selected={selected}>
{children}
</li>
);
}
function Wizard(props) {
const { onClose, onChange, pageContentClassName } = props;
function onClick(index) {
dispatch({ type: SET_CURRENT_STEP, currentStep: index });
// setSelected(index);
}
//get the progressBar steps
const steps = React.Children.map(props.children, page => {
const { id, label, description } = page.props;
return <div id={id} label={label} description={description} />;
});
function getContentAt(index) {
return stepContentWithProps[index];
}
const stepsWithProps = React.Children.map(props.children, (step, index) => {
const newStep = React.cloneElement(step, {});
return newStep;
});
const stepContentWithProps = stepsWithProps.map((step, index) => {
const { children } = step.props;
return (
<StepContent key={index} className={pageContentClassName}>
{children}
</StepContent>
);
});
const initialState = {
maxSteps: React.Children.count(props.children),
currentStep: 0
};
const [wizardData, dispatch] = useReducer(wizardReducer, initialState);
return (
<div className="wizard">
<p>This text is in wizard: currentStep={wizardData.currentStep}</p>
<WizardContext.Provider value={{ wizardData, dispatch }}>
<div className="wizard__upper">
<ul currentIndex={wizardData.currentStep} onChange={onClick}>
{steps}
</ul>
</div>
<div className="wizard__separator" />
<div className="wizard__content">{stepsWithProps}</div>
<div>
<button onClick={() => dispatch({ type: BACK })}>Back</button>
<button onClick={() => dispatch({ type: NEXT })}>Next</button>
</div>
</WizardContext.Provider>
</div>
);
}
Wizard.propTypes = {
/**
* Specify the text to be read by screen-readers when visiting the <Tabs>
* component
*/
ariaLabel: PropTypes.string,
/**
* Pass in a collection of <Tab> children to be rendered depending on the
* currently selected tab
*/
children: PropTypes.node,
/**
* Provide a className that is applied to the <PageContent> components
*/
pageContentClassName: PropTypes.string
};
export default Wizard;
WizardContext.jsx
import React, { createContext } from "react";
export const WizardContext = React.createContext(null);
export const SET_MAX_STEPS = "SET_MAX_STEPS";
export const SET_CURRENT_STEP = "SET_CURRENT_STEP";
export const BACK = "BACK";
export const NEXT = "NEXT";
export const SHOW_BACK = "SHOW_BACK";
export const SHOW_NEXT = "SHOW_NEXT";
export function wizardReducer(state, action) {
switch (action.type) {
case SET_MAX_STEPS:
return {
...state,
maxSteps: action.maxSteps
};
case SET_CURRENT_STEP:
if (action.currentStep >= state.maxSteps) return state;
return {
...state,
currentStep: action.currentStep
};
case BACK:
if (state.currentStep === 0) return state;
return {
...state,
currentStep: state.currentStep - 1
};
case NEXT:
if (state.currentStep >= state.maxSteps - 1) return state;
return {
...state,
currentStep: state.currentStep + 1
};
default:
return state;
}
}
索引.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import Wizard from "./Wizard";
import Cmp2 from "./Cmp2";
function App() {
const [wizardVisible, setWizardVisible] = useState(false);
return (
<div className="App">
<h1>
Wizard: why cant I see currentStep in wizard
<br />
(WORKING NOW!!!)
</h1>
<Wizard>
<div label="ddd">This is step1</div>
<Cmp2 />
<div label="ddd">This is step3</div>
</Wizard>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Cmp2.jsx
import React, { useState, useContext, useEffect } from "react";
import { WizardContext, SET_CURRENT_STEP } from "./WizardContext";
function Cmp2(props) {
const { wizardData, dispatch } = useContext(WizardContext);
return (
<div>
<br />
<p>This is Step 2</p>
{`in step2 (inner child of wizard): cur=${wizardData.currentStep}`}
<br />
<button
onClick={() => dispatch({ type: SET_CURRENT_STEP, currentStep: 1 })}
>
Click me to change current step
</button>
<br />
<br />
</div>
);
}
export default Cmp2;
現在我需要找到如何使其可訪問,我的意思是,它工作得很好,但是當我嘗試創建一個自定義鈎子(導入上下文)時,上下文是 null,當嘗試使用自定義鈎子時(這是可以理解的,因為它是在提供者之前在向導中調用的),如何在這里添加更好的功能?
這是一個可行的解決方案(沒有鈎子):
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.