24

我正在使用graphqlreact apollo 提供的 HOC 获取数据列表。例如:

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

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

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

我想到的一些选择:

选项1

APOLLO_QUERY_RESULT我应该在我的 Redux reducer 中监听操作吗?但这有点尴尬,因为这样我就需要听两者APOLLO_QUERY_RESULTAPOLLO_QUERY_RESULT_CLIENT如果查询之前已经运行过。而且operationName道具只出现在APOLLO_QUERY_RESULT动作中而不是APOLLO_QUERY_RESULT_CLIENT动作中。所以我需要剖析每一个APOLLO_QUERY_RESULT_CLIENT动作,才能知道它是从哪里来的。难道没有一种简单直接的方法来识别查询结果操作吗?

选项 2

我是否应该分派一个单独的动作SELECT_LIST_ITEMcomponentWillReceiveProps例如(使用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

我是否应该通过注入 Apollo 客户端直接使用它,withApollo然后使用client.query(...).then(result => { /* some logic */ selectItem(...)}). 但是我会失去 react-apollo 集成的所有好处,所以不是一个真正的选择。

选项 4

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

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

4

6 回答 6

1

我会监听 componentDidUpdate 中的更改,并在它们发生时调度一个将在 Redux 存储中设置 selectedItem 的操作

componentDidUpdate(prevProps, prevState) {

    if (this.props.data !== prevProps.data) {
        dispatch some action that set whatever you need to set
    }
}
于 2018-08-26T18:11:31.773 回答
1

我会做类似于Option 2的事情,但将生命周期方法放入实际的组件中。这样生命周期中的业务逻辑将与继承自 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)
于 2017-03-27T04:24:55.633 回答
0

在我看来,最好的方法是创建选项 2的hoc的稍微修改和可组合的版本,其使用方式与hoc类似。这是一个想到的示例用法:graphql

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

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

于 2018-09-13T00:14:16.083 回答
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,
        }
      },
    }
  ),
);
于 2018-07-27T22:55:55.443 回答
0

我过去遇到过类似的问题,并选择了类似于选项 2 的内容。如果您同时拥有自己的 redux 存储和 apollo 自己的内部存储,那么它们之间的同步状态就会成为问题。

如果您使用的是 apollo,我建议您摆脱自己的 redux 商店。如果同时依赖gql server和一些rest server,在逻辑上和物理上分离数据。

一旦你决定使用 apollo 作为你的“数据源”,调度就是突变,获取状态只是查询。您还可以使用查询过滤、排序等

于 2018-12-12T09:32:50.987 回答
0

我使用 hoc,它是选项 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 基于两个 Props isData 和 isLoading 的 hoc 渲染组件。如果 isData 为 true,那么它会渲染 Wrapped Component,否则渲染加载器。

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

我在 Component 的 componentWillMount 方法中设置了 dataList 的第一项。在我们获得由 withLoader hoc 确保的 dataList 之前,该组件不会挂载。

于 2018-09-12T20:42:06.177 回答