在React-Apollo中通过Redux获取变量调用GraphQL查询的问题
我的问题情况
我现在碰到一个棘手的问题:我的React组件需要调用GraphQL查询,但查询依赖的变量只能从Redux Store里获取。我已经尝试用react-apollo的compose把Redux的connect和graphql高阶组件结合起来用,但不仅没法从reducer拿到数据,连查询都加载不起来。而且看起来我的Store是空的,想确认是不是Redux的使用方式出了问题。
我的代码示例如下:
class abc extends Component { componentWillMount() { console.log(this.props.variableData); // 这里拿到的是空对象 } } function mapDispatchToProps(dispatch) { return bindActionCreators({ variableDataFetchAction }, dispatch); } function mapStateToProps(state) { return { variableData: state.reducerName.variableData, } } export default compose( connect(mapStateToProps, mapDispatchToProps), graphql(MyQuery, { options: props => { return { ....., variables: props.variableData, }; }, }), )(abc);
可能的问题原因与解决办法
我帮你梳理几个最可能的问题点,你可以逐一排查:
1. compose的执行顺序搞反了
这是最常见的坑!compose的执行逻辑是从右到左的,你现在把connect放在最左边,graphql放在右边,这意味着graphql高阶组件会先对组件进行包装,这时候组件还没和Redux建立连接,自然拿不到props.variableData。
修正方法:调换两者的顺序,让connect先执行(也就是放在compose的最右侧),把Redux的数据注入props后,再传给graphql的配置:
export default compose( graphql(MyQuery, { options: props => { // 现在这里就能拿到Redux注入的props了 return { variables: props.variableData, }; }, }), connect(mapStateToProps, mapDispatchToProps), )(abc);
2. 异步数据加载的时机问题
如果variableData是通过异步action(比如你的variableDataFetchAction)获取的,那在componentWillMount这个生命周期里,数据大概率还没请求回来,所以打印出来是空对象很正常。而且componentWillMount已经被React官方废弃了,建议换成componentDidMount。
修正方法:
- 在
componentDidMount里触发异步action获取数据 - 在
graphql的options里加判断,只有当variableData有有效数据时才执行查询,否则跳过:
graphql(MyQuery, { options: props => { // 检查variableData是否有有效内容,没有的话跳过查询 const hasValidData = Object.keys(props.variableData).length > 0; return { skip: !hasValidData, variables: props.variableData, }; }, }),
3. Redux Store的配置问题
如果Store本身是空的,那就要检查这几点:
- 确认创建Store时,用
combineReducers把你的reducerName正确合并进去了,比如:
import { combineReducers, createStore } from 'redux'; import reducerName from './yourReducer'; const rootReducer = combineReducers({ reducerName, // 这里要确保key和你mapStateToProps里的一致 }); const store = createStore(rootReducer);
- 检查
variableDataFetchAction是否正确触发,对应的reducer有没有处理这个action的类型,把数据正确存入state.reducerName.variableData里。 - 可以用Redux DevTools查看Store的状态变化,直观看到
variableData有没有被正确存入。
4. Provider的包裹问题
确保你的组件被正确包裹在Redux的Provider和Apollo的ApolloProvider里面,比如:
import { Provider } from 'react-redux'; import { ApolloProvider } from '@apollo/client'; import store from './store'; import client from './apolloClient'; ReactDOM.render( <Provider store={store}> <ApolloProvider client={client}> <App /> </ApolloProvider> </Provider>, document.getElementById('root') );
修正后的完整代码示例
import React, { Component } from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import { graphql, compose } from '@apollo/client/react/hoc'; import MyQuery from './MyQuery'; import { variableDataFetchAction } from './actions'; class abc extends Component { componentDidMount() { // 触发异步action获取数据 this.props.variableDataFetchAction(); } componentDidUpdate(prevProps) { // 监听variableData的变化,调试用 if (prevProps.variableData !== this.props.variableData) { console.log('最新的variableData:', this.props.variableData); } } render() { // 根据查询状态渲染内容 if (this.props.data.loading) return <div>加载中...</div>; if (this.props.data.error) return <div>出错了:{this.props.data.error.message}</div>; return <div>查询结果:{JSON.stringify(this.props.data)}</div>; } } function mapDispatchToProps(dispatch) { return bindActionCreators({ variableDataFetchAction }, dispatch); } function mapStateToProps(state) { // 给个默认空对象,避免undefined return { variableData: state.reducerName.variableData || {}, }; } export default compose( graphql(MyQuery, { options: props => { const hasValidData = Object.keys(props.variableData).length > 0; return { skip: !hasValidData, variables: props.variableData, }; }, }), connect(mapStateToProps, mapDispatchToProps), )(abc);
内容的提问来源于stack exchange,提问作者Naveen Vignesh

