IT이야기

렌더 함수 외부에 있는 액세스 리액션 컨텍스트

cyworld 2022. 3. 8. 21:59
반응형

렌더 함수 외부에 있는 액세스 리액션 컨텍스트

Redex 대신 새로운 Resact Concontext API를 사용하여 새로운 앱을 개발하고 있으며, 이전과 함께Redux예를 들어, 사용자 목록을 얻어야 할 때, 나는 단순히 전화를 한다.componentDidMount내 액션, 그러나 지금 리액션 컨텍스트와 함께 내 액션은 렌더링 기능 안에 있는 내 컨슈머 안에 살고 있다. 즉, 렌더 기능이 호출될 때마다 사용자 목록을 얻기 위해 내 액션에 전화를 걸 것이고, 그것은 좋지 않다. 왜냐하면 나는 많은 부적절한 요청을 할 것이기 때문이다.

그래서, 내가 어떻게 단 한 번만 내 행동이라고 부를 수 있을까?componentDidMount렌더링을 부르는 대신에?

예를 들어, 이 코드를 보십시오.

내가 모든 것을 싸고 있다고 가정해 보자.Providers다음과 같은 하나의 구성 요소:

import React from 'react';

import UserProvider from './UserProvider';
import PostProvider from './PostProvider';

export default class Provider extends React.Component {
  render(){
    return(
      <UserProvider>
        <PostProvider>
          {this.props.children}
        </PostProvider>
      </UserProvider>
    )
  }
}

그런 다음 이 Provider 구성 요소를 내 모든 앱을 다음과 같이 포장한다.

import React from 'react';
import Provider from './providers/Provider';
import { Router } from './Router';

export default class App extends React.Component {
  render() {
    const Component = Router();
    return(
      <Provider>
        <Component />
      </Provider>
    )
  }
}

예를 들어, 제 사용자 관점에서는 다음과 같은 것이 될 겁니다.

import React from 'react';
import UserContext from '../contexts/UserContext';

export default class Users extends React.Component {
  render(){
    return(
      <UserContext.Consumer>
        {({getUsers, users}) => {
          getUsers();
          return(
            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
          )
        }}
      </UserContext.Consumer>
    )
  }
}

내가 원하는 것은 이것이다.

import React from 'react';
import UserContext from '../contexts/UserContext';

export default class Users extends React.Component {
  componentDidMount(){
    this.props.getUsers();
  }

  render(){
    return(
      <UserContext.Consumer>
        {({users}) => {
          getUsers();
          return(
            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
          )
        }}
      </UserContext.Consumer>
    )
  }
}

그러나 물론 위의 예는 그 까닭에 효과가 없다.getUsers사용자 보기 소품에서 살지 마십시오.이것이 조금이라도 가능하다면 어떻게 하는 것이 올바른 방법인가?

편집: v16.8.0에 리액션 후크가 도입되면 후크를 사용하여 기능 구성 요소에 컨텍스트를 사용할 수 있음

const Users = () => {
    const contextValue = useContext(UserContext);
    // rest logic here
}

편집: 버전 16.6.0 이후 버전.라이프사이클 방법에서 컨텍스트를 사용할 수 있는 방법this.context맘에 들다

class Users extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* perform a side-effect at mount using the value of UserContext */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* render something based on the value of UserContext */
  }
}
Users.contextType = UserContext; // This part is important to access context values

버전 16.6.0 이전에 다음과 같은 방법으로 수행할 수 있다.

Lifecyle 메소드에서 컨텍스트를 사용하려면 다음과 같이 구성 요소를 쓰십시오.

class Users extends React.Component {
  componentDidMount(){
    this.props.getUsers();
  }

  render(){
    const { users } = this.props;
    return(

            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
    )
  }
}
export default props => ( <UserContext.Consumer>
        {({users, getUsers}) => {
           return <Users {...props} users={users} getUsers={getUsers} />
        }}
      </UserContext.Consumer>
)

일반적으로 앱에서 하나의 컨텍스트를 유지하며, 위의 로그인을 다시 사용하기 위해 HOC로 패키징하는 것이 타당하다.이렇게 쓰면 된다.

import UserContext from 'path/to/UserContext';

const withUserContext = Component => {
  return props => {
    return (
      <UserContext.Consumer>
        {({users, getUsers}) => {
          return <Component {...props} users={users} getUsers={getUsers} />;
        }}
      </UserContext.Consumer>
    );
  };
};

이렇게 쓰면 되잖아.

export default withUserContext(User);

그래, 난 한계를 가지고 이걸 할 방법을 찾았어.With thewith-context라이브러리 나는 모든 소비자 데이터를 구성 요소 소품에 간신히 삽입했다.

그러나 동일한 구성요소에 둘 이상의 소비자를 삽입하기 위해서는 이 라이브러리로 혼합 소비자를 만들어야 하는데, 이는 코드를 우아하게 만들지 못하고 생산적이지 않다.

이 라이브러리에 대한 링크: https://github.com/SunHuawei/with-context

편집: 사실 당신은 멀티 컨텍스트 api를 사용할 필요가 없다.with-context사실, 당신은 간단한 api를 사용하고 각각의 컨텍스트에 대해 장식가를 만들 수 있으며, 만약 당신이 당신의 컴포넌트에 둘 이상의 소비자를 사용하고 싶다면, 당신이 원하는 만큼 당신의 클래스 위에 선언하라!

내 입장에서 말하자면, 그것은 추가하기에 충분했다..bind(this)내 컴포넌트는 이렇게 생겼어.

// Stores File
class RootStore {
   //...States, etc
}
const myRootContext = React.createContext(new RootStore())
export default myRootContext;


// In Component
class MyComp extends Component {
  static contextType = myRootContext;

  doSomething() {
   console.log()
  }

  render() {
    return <button onClick={this.doSomething.bind(this)}></button>
  }
}

다음은 나에게 효과가 있다.useContext 및 useEnjectioner 후크를 사용하는 HORK 입니다.이 예에서는 소켓과 상호 작용하는 방법도 있다.

두 개의 컨텍스트(한 개는 파견용, 한 개는 국가용)를 만들고 있다.먼저 SampleProvider HOC로 일부 외부 구성 요소를 포장하십시오.그런 다음 하나 이상의 유틸리티 기능을 사용하여 상태 및/또는 디스패치에 액세스할 수 있다.withSampleContext파견과 주(州)를 모두 통과하기 때문에 좋다.또한 다음과 같은 다른 기능도 있다.useSampleState그리고useSampleDispatch기능 구성 요소 내에서 사용할 수 있는.

이 접근 방식을 사용하면 컨텍스트별 구문을 삽입할 필요 없이 항상 필요한 대로 리액션 구성 요소를 코딩할 수 있다.

import React, { useEffect, useReducer } from 'react';
import { Client } from '@stomp/stompjs';
import * as SockJS from 'sockjs-client';

const initialState = {
  myList: [],
  myObject: {}
};

export const SampleStateContext = React.createContext(initialState);
export const SampleDispatchContext = React.createContext(null);

const ACTION_TYPE = {
  SET_MY_LIST: 'SET_MY_LIST',
  SET_MY_OBJECT: 'SET_MY_OBJECT'
};

const sampleReducer = (state, action) => {
  switch (action.type) {
    case ACTION_TYPE.SET_MY_LIST:
      return {
        ...state,
        myList: action.myList
      };
    case ACTION_TYPE.SET_MY_OBJECT:
      return {
        ...state,
        myObject: action.myObject
      };
    default: {
      throw new Error(`Unhandled action type: ${action.type}`);
    }
  }
};

/**
 * Provider wrapper that also initializes reducer and socket communication
 * @param children
 * @constructor
 */
export const SampleProvider = ({ children }: any) => {
  const [state, dispatch] = useReducer(sampleReducer, initialState);
  useEffect(() => initializeSocket(dispatch), [initializeSocket]);

  return (
    <SampleStateContext.Provider value={state}>
      <SampleDispatchContext.Provider value={dispatch}>{children}</SampleDispatchContext.Provider>
    </SampleStateContext.Provider>
  );
};

/**
 * HOC function used to wrap component with both state and dispatch contexts
 * @param Component
 */
export const withSampleContext = Component => {
  return props => {
    return (
      <SampleDispatchContext.Consumer>
        {dispatch => (
          <SampleStateContext.Consumer>
            {contexts => <Component {...props} {...contexts} dispatch={dispatch} />}
          </SampleStateContext.Consumer>
        )}
      </SampleDispatchContext.Consumer>
    );
  };
};

/**
 * Use this within a react functional component if you want state
 */
export const useSampleState = () => {
  const context = React.useContext(SampleStateContext);
  if (context === undefined) {
    throw new Error('useSampleState must be used within a SampleProvider');
  }
  return context;
};

/**
 * Use this within a react functional component if you want the dispatch
 */
export const useSampleDispatch = () => {
  const context = React.useContext(SampleDispatchContext);
  if (context === undefined) {
    throw new Error('useSampleDispatch must be used within a SampleProvider');
  }
  return context;
};

/**
 * Sample function that can be imported to set state via dispatch
 * @param dispatch
 * @param obj
 */
export const setMyObject = async (dispatch, obj) => {
  dispatch({ type: ACTION_TYPE.SET_MY_OBJECT, myObject: obj });
};

/**
 * Initialize socket and any subscribers
 * @param dispatch
 */
const initializeSocket = dispatch => {
  const client = new Client({
    brokerURL: 'ws://path-to-socket:port',
    debug: function (str) {
      console.log(str);
    },
    reconnectDelay: 5000,
    heartbeatIncoming: 4000,
    heartbeatOutgoing: 4000
  });

  // Fallback code for http(s)
  if (typeof WebSocket !== 'function') {
    client.webSocketFactory = function () {
      return new SockJS('https://path-to-socket:port');
    };
  }

  const onMessage = msg => {
    dispatch({ type: ACTION_TYPE.SET_MY_LIST, myList: JSON.parse(msg.body) });
  };

  client.onConnect = function (frame) {
    client.subscribe('/topic/someTopic', onMessage);
  };

  client.onStompError = function (frame) {
    console.log('Broker reported error: ' + frame.headers['message']);
    console.log('Additional details: ' + frame.body);
  };
  client.activate();
};

상위 상위 상위 구성 요소의 컨텍스트를 통과해야 하위 구성 요소에서 소품으로 액세스할 수 있다.

참조URL: https://stackoverflow.com/questions/49809884/access-react-context-outside-of-render-function

반응형