首页 文章

如何在Apollo GraphQL查询返回后更新Redux存储

提问于
浏览
15

我正在使用react apollo提供的 graphql HOC获取数据列表 . 例如 . :

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

我在受控的单选按钮组中显示列表,我需要默认选择其中一个项目 . 所选项目的 id 保留在Redux存储中 .

那么,问题是如何在查询成功返回后更新Redux存储(即设置 selectedItem )?

我想到了一些选择:

选项1

我应该在我的Redux减速机中监听 APOLLO_QUERY_RESULT 动作吗?但这有点尴尬,因为如果查询之前已经运行过,我需要同时收听 APOLLO_QUERY_RESULTAPOLLO_QUERY_RESULT_CLIENT . 并且 operationName prop仅出现在 APOLLO_QUERY_RESULT 动作中而不是 APOLLO_QUERY_RESULT_CLIENT 动作中 . 因此,我需要剖析每一个 APOLLO_QUERY_RESULT_CLIENT 动作,以了解它的来源 . 是否有一种简单直接的方法来识别查询结果操作?

选项2

我应该在 componentWillReceiveProps 中发送单独的操作,如 SELECT_LIST_ITEM ,例如(使用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);
      }
    }
  })
);

选项3

我应该直接使用 withApollo 来使用Apollo客户端,然后使用 client.query(...).then(result => { /* some logic */ selectItem(...)}) 调度我的操作 . 但是,我会放弃react-apollo集成的所有好处,所以不是一个真正的选择 .

选项4

在查询返回后,我是否应该根本不更新Redux存储?因为我也可以实现一个选择器,如果它被设置则返回 selectedItem 如果没有,它会尝试通过浏览商店的 apollo 部分来派生它 .

我的选择都不能让我满意 . 那么,我该怎么做呢?

6 回答

  • 0

    我会做类似于 Option 2 的事情,但将生命周期方法放入实际的Component中 . 这样,生命周期中的业务逻辑将与从Container继承的props分离 .

    所以像这样:

    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)
    
  • 0

    应该有足够的'道具',如:

    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,
            }
          },
        }
      ),
    );
    
  • 0

    我会听取componentDidUpdate中的更改,当它们发生调度时,会在Redux商店中设置selectedItem

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

    我使用了一个稍微好一点的选项2版本 . 我在撰写结束时使用withLoader Hoc .

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

    WithLoader hoc渲染组件基于两个Props isData和isLoading . 如果isData为true,则呈现Wrapped Component,否则呈现加载器 .

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

    我在Component的componentWillMount方法中设置了dataList的第一项 . 组件不会挂载,直到我们得到dataList,这是由withLoader hoc确保的 .

  • 0

    在我看来,最好的方法是创建 hocOption 2 的稍微修改和可组合的版本,它将与 graphql hoc类似地使用 . 以下是我想到的一个示例用法:

    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)
    

    最简单的实现将是这样的:

    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)
    }
    

    即使我没有尝试过这个伪代码,它也没有测试,甚至没有完成,它背后的想法非常简单易懂 . 希望它能帮助别人

  • 0

    我在过去遇到了类似的问题并选择了类似于选项2的东西 . 如果你有自己的redux商店和apollo自己的内部商店,那么在它们之间同步状态就成了问题 .

    如果你使用阿波罗,我建议你摆脱你自己的redux商店 . 如果您同时依赖gql服务器和一些其他服务器,请在逻辑上和物理上分离数据 .

    一旦你决定使用apollo作为你的'数据源',调度只是变异,获取状态只是查询 . 您还可以使用查询进行筛选,排序等

相关问题