Programing

react useContext 를 사용하여 컨텍스트의 데이터를 동일한 구성 요소에서 표시하는 방법

c10106 2022. 3. 17. 20:52
반응형

react useContext 를 사용하여 컨텍스트의 데이터를 동일한 구성 요소에서 표시하는 방법

리액션 마법사 구성 요소를 만들고 있으며, 컨텍스트를 사용하여 부모와 자식 간에 데이터를 전달하고 싶다.

그래서 마법사, 컨텍스트, 공급자, 사용자 지정 후크를 만들었는데 문제는 컨텍스트를 사용하려고 하면 마법사 구성 요소에 올바른 정보가 표시되지 않는다는 겁니다.

(https://codesandbox.io/embed/wizardwitcontext-rfpui 참조)

사용자 지정 후크로 로그인을 전송할 수 있도록 마법사 자체의 컨텍스트 데이터에 의존하는 방법

wizard.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;

Wizard.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


당신이 전화하는 것은useContext같은 수준으로Context.Provider:

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>
  );
}

당신은 당신의 구조를 바꾸고 전화를 해야 한다.useContext의 범위 내에서Provider아이들.

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, 을 참조하십시오.

컨텍스트는 구성요소 트리 위에 있는 가장 가까운 공급자에서 파생된다.리액션 문서로부터.

const 값 = useContext(MyContext);

컨텍스트 개체(React.createContext에서 반환된 값)를 수락하고 해당 컨텍스트에 대한 현재 컨텍스트 값을 반환하십시오.현재 컨텍스트 값은 트리의 호출 구성요소 위에 가장 가까운 값의 받침대에 의해 결정된다.

이 경우에는 두 가지 옵션이 있다.

1.공급자에서 앱(index.js) 구성요소를 줄여야 한다.

또는

2.Wizard 구성 요소를 제공자가 되게 하고 하위 구성 요소에 useContext 후크를 사용하십시오.

데모: https://stackblitz.com/edit/react-msac8q

이게 도움이 되길 바래.

제공자와 동일한 구성 요소에서 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에 설명된 해결책은 마법사 파일에 환원기를 만드는 것이다. 그러면 마법사가 데이터에 액세스할 수 있고, 또한 현대판:

마법사.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이 된다(제공자보다 먼저 마법사 호출되기 때문에 이해할 수 있음), 여기서 더 나은 기능을 추가하는 방법은 무엇인가?

여기 (고리가 없는) 작동 솔루션이 있다.

https://codesandbox.io/embed/wizardwitcontext-working-3lxhd

참조URL: https://stackoverflow.com/questions/58152818/how-can-i-use-react-usecontext-to-show-data-from-context-in-the-same-componen

반응형