programing

HOC - 기능 컴포넌트

sourcejob 2023. 3. 20. 23:11
반응형

HOC - 기능 컴포넌트

후 리액트 앱으로 HOC를 작성했습니다만, 정상적으로 동작하고 있습니다.다만, 기능 컴포넌트로서 HOC를 작성할 수 있는 방법이 있을까요(상태의 유무에 관계없이)--?는 예를 들어 클래스 기반 컴포넌트이기 때문입니다.

웹상에서 같은 것을 찾으려고 했지만 아무것도 얻을 수 없었다.그게 가능하기나 하냐고?아니면 옳은 일이란 말인가?

어떤 잠재 고객이라도 감사히 받겠습니다:)

나는 siraj에 동의한다, 엄밀히 말하면 받아들여진 답변의 예는 진정한 HOC가 아니다.HOC의 구별되는 기능은 컴포넌트를 반환하는 반면,PrivateRoute승인된 답변의 구성요소는 구성요소 자체입니다.따라서, 이것은, 목적의 달성은 훌륭하지만, HOC의 좋은 예라고는 생각하지 않습니다.

기능하는 컴포넌트의 세계에서 가장 기본적인 HOC은 다음과 같습니다.

const withNothing = Component => ({ ...props }) => (
  <Component {...props} />
);

부르기withNothing는 다른 컴포넌트(인스턴스가 아닌 주요 차이점)를 반환하며 일반 컴포넌트와 동일하게 사용할 수 있습니다.

const ComponentWithNothing = withNothing(Component);
const instance = <ComponentWithNothing someProp="test" />;

이를 사용하는 한 가지 방법은 애드혹(말장난 의도 없음) 컨텍스트 공급자를 사용하는 것입니다(웃음).

내 응용 프로그램에 사용자가 로그인할 수 있는 여러 지점이 있다고 가정해 보겠습니다.로그인 로직(API 호출 및 성공/오류 메시지)을 이 모든 포인트에 복사하고 싶지 않기 때문에 재사용을 원합니다.<Login />요소.단, 이 로그인 포인트는 모두 시각적으로 크게 다르기 때문에 재사용 가능한 컴포넌트는 사용할 수 없습니다.내가 필요한 것은 재사용할 수 있는 것이다.<WithLogin />컴포넌트는 API 호출과 성공/오류 메시지 등 자녀에게 필요한 모든 기능을 제공합니다.이를 위한 한 가지 방법은 다음과 같습니다.

// This context will only hold the `login` method.
// Calling this method will invoke all the required logic.
const LoginContext = React.createContext();
LoginContext.displayName = "Login";

// This "HOC" (not a true HOC yet) should take care of
// all the reusable logic - API calls and messages.
// This will allow me to pass different layouts as children.
const WithLogin = ({ children }) => {
  const [popup, setPopup] = useState(null);

  const doLogin = useCallback(
    (email, password) =>
      callLoginAPI(email, password).then(
        () => {
          setPopup({
            message: "Success"
          });
        },
        () => {
          setPopup({
            error: true,
            message: "Failure"
          });
        }
      ),
    [setPopup]
  );

  return (
    <LoginContext.Provider value={doLogin}>
      {children}

      {popup ? (
        <Modal
          error={popup.error}
          message={popup.message}
          onClose={() => setPopup(null)}
        />
      ) : null}
    </LoginContext.Provider>
  );
};

// This is my main component. It is very neat and simple
// because all the technical bits are inside WithLogin.
const MyComponent = () => {
  const login = useContext(LoginContext);

  const doLogin = useCallback(() => {
    login("a@b.c", "password");
  }, [login]);

  return (
    <WithLogin>
      <button type="button" onClick={doLogin}>
        Login!
      </button>
    </WithLogin>
  );
};

유감스럽게도, 이것은 동작하지 않습니다.LoginContext.Provider안에서 인스턴스화되다 MyComponent, 등useContext(LoginContext)아무것도 반환하지 않습니다.

구조 현장 출동!작은 중간상인을 하나 더하면 어떨까?

const withLogin = Component => ({ ...props }) => (
  <WithLogin>
    <Component {...props} />
  </WithLogin>
);

그 후:

const MyComponent = () => {
  const login = useContext(LoginContext);

  const doLogin = useCallback(() => {
    login("a@b.c", "password");
  }, [login]);

  return (
    <button type="button" onClick={doLogin}>
      Login!
    </button>
  );
};

const MyComponentWithLogin = withLogin(MyComponent);

빵!MyComponentWithLogin이제 예상대로 작동합니다.

이게 이 상황에 접근하는 최선의 방법은 아닐지 몰라도 난 좀 맘에 들어.

네, 그냥 기능 추가 호출일 뿐이죠. 그 이상은 아닙니다.공식 가이드에 따르면:

HOC는 그 자체로는 React API의 일부가 아닙니다.그것들은 리액트의 구성적 성격에서 나타나는 패턴이다.

예를 들어 컴포넌트를 입력으로 받아들이고 다른 컴포넌트를 출력으로 반환하는 기능적인 스테이트리스 컴포넌트를 작성할 수 있습니다.

  1. 사용자의 인증 여부에 따라 구성 요소를 프로펠러 값으로 받아들이고 다른 구성 요소를 반환하는 PrivateRoute 구성 요소를 생성할 수 있습니다.
  2. 되지 않은 ), .로그인 페이지에는, 「」( 「콘텍스트스토어에서 읽기」)가 표시됩니다.<Redirect to='/login'/>그.<Component {...props} />

App.js

const App = () => {
  return (
      <Switch>
        <PrivateRoute exact path='/' component={Home} />
        <Route exact path='/about' component={About} />
        <Route exact path='/login' component={Login} />
        <Route exact path='/register' component={Register} />
      </Switch>
  );
}

export default App;

PrivateRoute.jsx

import React, { useContext , useEffect} from 'react';
import { Route, Redirect } from 'react-router-dom'
import AuthContext from '../../context/auth/authContext'

const PrivateRoute = ({ component: Component, ...rest }) => {
  const authContext = useContext(AuthContext)
  const { loadUser, isAuthenticated } = authContext
  useEffect(() => {
    loadUser()
    // eslint-disable-next-line
  }, [])
  if(isAuthenticated === null){
    return <></>
  }
  return (
    <Route {...rest} render={props =>
      !isAuthenticated ? (
        <Redirect to='/login'/>
      ) : (
        <Component {...props} />
      )
    }
    />
  );
};
export default PrivateRoute;

고차 컴포넌트가 클래스 컴포넌트일 필요는 없습니다.그 목적은 일부 논리에 따라 컴포넌트를 입력으로 취득하고 출력으로 컴포넌트를 반환하는 것입니다.

다음으로 기능 컴포넌트에서 HOC를 사용하는 방법을 지나치게 단순화한 예를 나타냅니다.

"포장"할 기능 구성 요소:

import React from 'react'
import withClasses from '../withClasses'

const ToBeWrappedByHOC = () => {
return (
    <div>
        <p>I'm wrapped by a higher order component</p>
    </div>
       )
}

export default withClasses(ToBeWrappedByHOC, "myClassName");

고차 컴포넌트:

import React from 'react'


const withClasses = (WrappedComponent, classes) => {
return (props) => (
    <div className={classes}>
        <WrappedComponent {...props} />
    </div>
       );
};

export default withClasses;

이와 같이 컴포넌트는 다른 컴포넌트에서 사용할 수 있습니다.

<ToBeWrappedByHOC/>

나는 파티에 늦을지도 모르지만 HOC에 대한 나의 2센트입니다.

  • 진정한 리액트 기능 컴포넌트 방식으로 HOC를 작성하는 것은 네스트된 함수 내에서 후크를 호출하지 않는 것이 권장되기 때문에 불가능합니다.

루프, 조건 또는 중첩된 함수에 Hooks를 호출하지 마십시오.대신, 초기 반환 전에 항상 리액트 함수의 최상위 수준에서 Hooks를 사용하십시오.이 규칙을 따르면 구성 요소가 렌더링할 때마다 후크가 동일한 순서로 호출됩니다.이를 통해 React는 여러 useState 호출과 useEffect 호출 사이의 Hook 상태를 올바르게 유지할 수 있습니다(궁금하신 분은 아래에 자세히 설명드리겠습니다).

후크 규칙

이것이 내가 시도했지만 실패한 것이다.

import React, { useState } from "react";

import "./styles.css";

function Component(props) {
  console.log(props);
  return (
    <div>
      <h2> Component Count {props.count}</h2>
      <button onClick={props.handleClick}>Click</button>
    </div>
  );
}

function Component1(props) {
  console.log(props);
  return (
    <div>
      <h2> Component1 Count {props.count}</h2>
      <button onClick={props.handleClick}>Click</button>
    </div>
  );
}

function HOC(WrapperFunction) {
  return function (props) {
    const handleClick = () => {
      setCount(count + 1);
    };

    const [count, setCount] = useState(0);
    return (
      <WrapperFunction handleClick={handleClick} count={count} {...props} />
    );
  }
}

const Comp1 = HOC((props) => {
  return <Component {...props} />;
});
const Comp2 = HOC((props) => {
  return <Component1 {...props} />;
});

export default function App() {
  return (
    <div className="App">
      <Comp1 name="hel" />
      <Comp2 />
    </div>
  );
}

코드 샌드 박스

코드는 codesandbox에서 동작하지만 위의 규칙 때문에 로컬머신에서는 실행되지 않지만 이 코드를 실행하려고 하면 다음 오류가 발생합니다.

React Hook "useState" cannot be called inside a callback

이 문제를 해결하기 위해 다음 작업을 수행했습니다.

import "./styles.css";
import * as React from "react";
//macbook
function Company(props) {
  return (
    <>
      <h1>Company</h1>
      <p>{props.count}</p>
      <button onClick={() => props.increment()}>increment</button>
    </>
  );
}

function Developer(props) {
  return (
    <>
      <h1>Developer</h1>
      <p>{props.count}</p>
      <button onClick={() => props.increment()}>increment</button>
    </>
  );
}

//decorator
function HOC(Component) {
  // return function () {
  //   const [data, setData] = React.useState();
  //   return <Component />;
  // };
  class Wrapper extends React.Component {
    constructor(props) {
      super(props);
      this.state = { count: 0 };
    }
    handleClick = () => {
      this.setState({ count: this.state.count + 1 });
    };
    render() {
      return (
        <Component count={this.state.count} increment={this.handleClick} />
      );
    }
  }
  return Wrapper;
}

const NewCompany = HOC(Company);
const NewDeveloper = HOC(Developer);

export default function App() {
  return (
    <div className="App">
      <NewCompany name={"Google"} />
      <br />
      <NewDeveloper />
    </div>
  );
}

코드 샌드박스

기능적인 컴포넌트는 잘 동작한다고 생각합니다.

import {useEffect, useState} from 'react';

// Target Component
function Clock({ time }) {
  return <h1>{time}</h1>
}

// HOC 
function app(C) {
  return (props) => {
    const [time, setTime] = useState(new Date().toUTCString());
    useEffect(() => {
      setTimeout(() => setTime(new Date().toUTCString()), 1000);
    })
    return <C {...props} time={time}/>
  }
}

export default app(Clock);

테스트하려면 https://codesandbox.io/s/hoc-s6kmnv 를 참조해 주세요.

네, 가능합니다.

import React, { useState } from 'react';

const WrapperCounter = OldComponent =>{
  function WrapperCounter(props){
    const[count,SetCount] = useState(0)
    const incrementCounter = ()=>{
        SetCount(count+1)
    }
    return(<OldComponent {...props} count={count} incrementCounter={incrementCounter}></OldComponent>)
  }
  return WrapperCounter
}

export default WrapperCounter

import React from 'react';
import WrapperCounter from './WrapperCounter';
function CounterFn({count,incrementCounter}){
    return(
        <button onClick={incrementCounter}>Counter inside functiona component {count}</button>
    )
}

export default WrapperCounter(CounterFn)

언급URL : https://stackoverflow.com/questions/57852370/hoc-functional-component

반응형