update react query que español and reactjs apollostack react-apollo apollo-client

reactjs - react - Cómo actualizar el almacén de Redux después de que devuelve la consulta de Apollo GraphQL



que es apollo graphql (6)

En el pasado, enfrenté un problema similar y elegí algo similar a la opción 2. Si tiene su propia tienda de redux y la tienda interna de Apollo, la sincronización de los estados entre ellos se convierte en un problema.

Yo sugeriría deshacerse de su propia tienda de redux si está utilizando apollo. Si confía en el servidor gql y en algunos servidores de descanso al mismo tiempo, separe los datos lógica y físicamente.

Una vez que decida utilizar apollo como su ''fuente de datos'', el envío es solo una mutación y el estado de obtención es solo una consulta. También puedes filtrar, ordenar, etc. con consultas.

Estoy obteniendo una lista de datos con el HOC de graphql proporcionado por reaccion apollo. P.ej:

const fetchList = graphql( dataListQuery, { options: ({ listId }) => ({ variables: { listId, }, }), props: ({ data: { loading, dataList } }) => { return { loading, list: dataList, }; } } );

Estoy mostrando la lista en un grupo de botones de radio controlado y debo seleccionar uno de los elementos de forma predeterminada. El id del artículo seleccionado se mantiene en la tienda Redux.

Entonces, la pregunta es ¿cómo actualizar el almacén de Redux (es decir, configurar el elemento selectedItem ) después de que la consulta se devuelva correctamente?

Algunas opciones que vinieron a mi mente:

Opción 1

¿Debo escuchar las acciones APOLLO_QUERY_RESULT en mi reductor de Redux? Pero eso es un poco incómodo porque entonces necesitaría escuchar tanto APOLLO_QUERY_RESULT como APOLLO_QUERY_RESULT_CLIENT si la consulta ya se ejecutó antes. Y también la propiedad operationName solo está presente en la acción APOLLO_QUERY_RESULT y no en la acción APOLLO_QUERY_RESULT_CLIENT . Así que necesitaría analizar cada acción APOLLO_QUERY_RESULT_CLIENT para saber de dónde vino. ¿No existe una forma sencilla y directa de identificar las acciones de resultados de consultas?

opcion 2

¿Debo enviar una acción separada como SELECT_LIST_ITEM en componentWillReceiveProps por ejemplo (usando recompose ):

const enhance = compose( connect( function mapStateToProps(state) { return { selectedItem: getSelectedItem(state), }; }, { selectItem, // action creator } ), graphql( dataListQuery, { options: ({ listId }) => ({ variables: { listId, }, }), props: ({ data: { loading, dataList } }) => ({ loading, items: dataList, }), } ), lifecycle({ componentWillReceiveProps(nextProps) { const { loading, items, selectedItem, selectItem, } = nextProps; if (!selectedItem && !loading && items && items.length) { selectItem(items[items.length - 1].id); } } }) );

Opcion 3

Debo utilizar el cliente Apollo directamente inyectándolo con withApollo y luego enviar mi acción con client.query(...).then(result => { /* some logic */ selectItem(...)}) . Pero luego perdería todos los beneficios de la integración react-apollo, así que no es realmente una opción.

Opcion 4

¿No debería actualizar la tienda de Redux después de que se devuelva la consulta? Porque también podría implementar un selector que devuelva el elemento selectedItem si está configurado y, si no, intenta derivarlo al navegar por la parte de apollo de la tienda.

Ninguna de mis opciones me satisface. Entonces, ¿cómo haría eso bien?


En mi opinión, el mejor enfoque es crear una versión modificada y modificable de hoc de la Opción 2 , que se utilizará de manera similar a graphql hoc. Aquí hay un ejemplo de uso que viene a la mente:

export default compose( connect( state => ({ /* ... */ }), dispatch => ({ someReduxAction: (payload) => dispatch({ /* ... */ }), anotherReduxAction: (payload) => dispatch({ /* ... */ }), }), ), graphqlWithDone(someQuery, { name: ''someQuery'', options: props => ({ /* ... */ }), props: props => ({ /* ... */ }), makeDone: props => dataFromQuery => props.someReduxAction(dataFromQuery) }), graphqlWithDone(anotherQuery, { name: ''anotherQuery'', options: props => ({ /* ... */ }), props: props => ({ /* ... */ }), makeDone: props => dataFromQuery => props.anotherReduxAction(dataFromQuery) }) )(SomeComponent)

Y la implementación más simple sería algo como esto:

const graphqlWithDone = (query, queryConfig) => (Wrapped) => { const enhance = graphql(query, { ...queryConfig, props: (props) => ({ queryData: { ...( props[queryConfig.name] || props.data ) }, queryProps: queryConfig.props(props), }) }) class GraphQLWithDone extends Component { state = { isDataHandled: false } get wrappedProps () { const resultProps = { ...this.props }; delete resultProps.queryData; delete resultProps.queryProps; return { ...resultProps, ...this.props.queryProps } } get shouldHandleLoadedData () { return ( !this.props.queryData.error && !this.props.queryData.loading && !this.state.isDataHandled ) } componentDidUpdate() { this.shouldHandleLoadedData && this.handleLoadedData(this.props.queryData); } handleLoadedData = (data) => { if (!makeDone || !isFunction(makeDone)) return; const done = makeDone(this.wrappedProps) this.setState({ isDataHandled: true }, () => { done(data) }) } render() { return <Wrapped {...this.wrappedProps} /> } } return enhance(GraphQLWithDone) }

Incluso aunque no haya probado este pseudocódigo, no tiene pruebas ni ha terminado, la idea que hay detrás es bastante sencilla y fácil de entender. Espero que ayude a alguien


Escucharía los cambios en componentDidUpdate y, cuando ocurrieran, enviaría una acción que establecerá selectedItem en la tienda Redux

componentDidUpdate(prevProps, prevState) { if (this.props.data !== prevProps.data) { dispatch some action that set whatever you need to set } }


Haría algo similar a la Opción 2 , pero pondría los métodos del ciclo de vida en el Componente real. De esta manera, la lógica de negocios en el ciclo de vida se separará de los accesorios heredados de Container.

Así que algo como esto:

class yourComponent extends Component{ componentWillReceiveProps(nextProps) { const { loading, items, selectedItem, selectItem, } = nextProps; if (!selectedItem && !loading && items && items.length) { selectItem(items[items.length - 1].id); } } render(){...} } // Connect redux and graphQL to the Component const yourComponentWithGraphQL = graphql(...)(yourComponent); export default connect(mapStateToProps, mapDispatchToProps)(yourComponentWithGraphQL)


Uso hoc, que es una versión ligeramente mejor de la opción 2. Utilizo withLoader Hoc al final de la composición.

const enhance = compose( connect(), graphql(dataListQuery, { options: ({ listId }) => ({ variables: { listId, }, }), props: ({ data: { loading, dataList } }) => ({ isLoading:loading, isData:!!dataList, dataList }), } ), withLoader )(Component)

WithLoader hoc render componente basado en dos Props isData y isLoading. Si isData es verdadero, entonces procesa el componente Wrapped o el cargador de render.

function withLoader(WrappedComponent) { class comp extends React.PureComponent { render(){ return this.props.isData?<WrappedComponent {...this.props}/>:<Loading/> } } }

Configuré el primer elemento de dataList en el método de componente de montaje de componentes. El componente no se monta hasta que obtengamos la lista de datos, que está garantizada por withLoader hoc.


debe haber suficiente para usar ''accesorios'', como:

const enhance = compose( connect( function mapStateToProps(state) { return { selectedItem: getSelectedItem(state), }; }, { selectItem, // action creator } ), graphql( dataListQuery, { options: ({ listId }) => ({ variables: { listId, }, }), props: ({ data: { loading, dataList } }) => { if (!loading && dataList && dataList.length) { selectItem(dataList[dataList.length - 1].id); } return { loading, items: dataList, } }, } ), );