렌더 함수 외부에 있는 액세스 리액션 컨텍스트
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
'IT이야기' 카테고리의 다른 글
Vuex 저장소를 교체하지 않고 Vuex 업데이트 (0) | 2022.03.08 |
---|---|
그룹별 판다 비율 (0) | 2022.03.08 |
Vue 및 Vuex가 스토어 모듈 내부에서 Axios 작업을 수행함 (0) | 2022.03.08 |
VueJS 조건부로 요소의 속성 추가 (0) | 2022.03.08 |
반응식 런오스가 시뮬레이터를 찾을 수 없음 (0) | 2022.03.08 |