Estou desenvolvendo um novo aplicativo usando a nova API React Context em vez de Redux, e antes, com Redux
, quando preciso obter uma lista de usuários, por exemplo, simplesmente chamo componentDidMount
minha ação, mas agora com React Context, minhas ações ficam dentro my Consumer que está dentro da minha função de renderização, o que significa que toda vez que minha função de renderização for chamada, ela chamará minha ação para obter minha lista de usuários e isso não é bom porque estarei fazendo muitas solicitações desnecessárias.
Então, como posso chamar apenas uma vez minha ação, como em em componentDidMount
vez de chamar em render?
Só para exemplificar, veja este código:
Vamos supor que estou envolvendo tudo Providers
em um único componente, assim:
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>
)
}
}
Então eu coloco esse componente Provider envolvendo todo o meu aplicativo, assim:
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>
)
}
}
Agora, na minha visualização de usuários, por exemplo, será algo assim:
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>
)
}
}
O que eu quero é isso:
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>
)
}
}
Mas é claro que o exemplo acima não funciona porque os getUsers
adereços não residem em meus usuários. Qual é a maneira certa de fazer isso, se isso for possível?
fonte
this.context
com vários contextos dentro do mesmo componente? Vamos supor que eu tenha um componente que precisa acessar UserContext e PostContext, como lidar com isso? O que posso ver é criar um contexto misturando os dois, mas essa é a única ou melhor solução para isso?Ok, encontrei uma maneira de fazer isso com uma limitação. Com o
with-context
biblioteca, consegui inserir todos os meus dados de consumidor em meus adereços de componente.Mas, para inserir mais de um consumidor no mesmo componente é complicado de fazer, você tem que criar consumidores mistos com esta biblioteca, o que torna o código não elegante e improdutivo.
O link para esta biblioteca: https://github.com/SunHuawei/with-context
EDITAR: Na verdade, você não precisa usar a API multi-contexto que
with-context
fornece, na verdade, você pode usar a API simples e fazer um decorador para cada um de seu contexto e se quiser usar mais de um consumidor em seu componente, basta declare acima de sua classe quantos decoradores você quiser!fonte
De minha parte, foi o suficiente para agregar
.bind(this)
ao evento. É assim que meu componente se parece.// 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> } }
fonte
O seguinte está funcionando para mim. Este é um HOC que usa ganchos useContext e useReducer. Também há uma maneira de interagir com os soquetes neste exemplo.
Estou criando 2 contextos (um para despacho e outro para estado). Você precisaria primeiro envolver algum componente externo com o SampleProvider HOC. Então, usando uma ou mais funções de utilitário, você pode obter acesso ao estado e / ou ao despacho. O
withSampleContext
é bom porque passa tanto no despacho quanto no estado. Existem também outras funções comouseSampleState
euseSampleDispatch
que podem ser usadas em um componente funcional.Essa abordagem permite que você codifique seus componentes React como sempre fez, sem a necessidade de injetar qualquer sintaxe específica do Context.
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(); };
fonte
2020+ com ganchos
Você pode acessar o contexto de hooks de reação fora de uma função de renderização, a única diferença é que seu componente não será renderizado novamente, mas pode ser propositalmente.
Em vez de fazer isso:
// store.js import React from 'react'; const UserContext = React.createContext({ user: {}, setUser: () => null }); export { UserContext };
Faça um fechamento:
// store.js import React from 'react'; let user = {}; const UserContext = React.createContext({ user, setUser: () => null }); export { UserContext };
então você pode fazer
import { UserContext } from 'store'; console.log(UserContext._currentValue.user);
É isso aí!
fonte
Você tem que passar o contexto no componente pai superior para obter acesso como adereços na criança.
fonte