react-apollo:根据用户输入(例如onClick)触发查询

时间:2019-05-13 23:55:19

标签: javascript reactjs apollo react-apollo apollo-client

(从https://spectrum.chat/apollo/react-apollo/firing-query-in-response-to-user-input-e-g-onclick~13ae09a8-6028-48cf-ac3f-3fbba2307ccc交叉发布)

我花了一个下午对我认为应该是一个相当简单的任务be之以鼻:根据用户输入触发查询;例如搜索字段。

我有一个可以在搜索字段中接受用户输入的应用程序。当用户单击“搜索”按钮时,我需要进行查询,将变量设置为该字段的值。该查询需要设置为network-only,以便它每次都获取新数据。查询完成后,我需要显示一个详细组件,该组件显示从服务器获取的数据。该详细信息视图包含两个<Mutation />组件,这些组件对用户数据进行更新。详细信息视图需要保持同步,以便反映当前状态。

我已经尝试了几种方法来解决这个问题,但是每种方法都可以使我部分地实现这一目标,但并非一路走来。我确定我缺少简单的东西。

方法1:<Query />组件

顶级应用程序组件看起来像这样(从实际简化):

const App = () => {
  const [searchFieldValue, setSearchFieldValue] = useState('');
  const [username, setUsername] = useState('');
  return (
    <Container>
      <Input
        placeholder="Username"
        name="username"
        id="username"
        value={searchFieldValue}
        width={6}
        onChange={(e, { value }) => {
          setSearchFieldValue(value);
        }}
        />
        <Button onClick={ () => { 
          setUsername(searchFieldInput)
        }>Search</Button>

        {username && <DetailView username={username} />
    </Container>
}

const DetailView = ({username}) => (
  <Query query={GET_USER} variables={{username}} fetchPolicy="network-only">
    {({data, loading, error}) => {
      if (loading) return <Loading />
      if (error) return <Error />
      const { user } = data;
      return (
      // display user data
      // includes a couple of buttons that trigger mutations on the user
      )
    }}
  </Query>
)

本质上,当单击“搜索”按钮时,我将username状态变量设置为搜索字段的内容。这将导致username不再是虚假的,并且DetailView组件已安装。

有效方法:

将查询发送到服务器,然后接收数据并将其存储在缓存中,并呈现视图。突变都可以正常工作,并且缓存将更新并显示结果。如果我在搜索输入中输入其他值,则会导致<App />的状态更改并重新呈现;子查询将重新呈现,并发送新查询,等等。

什么不起作用:

我无法两次搜索同一用户。例如,假设我在搜索字段中输入“ janesmith”。我得到了Jane的信息,并且呈现了<DetailView />。如果我想通过再次单击搜索按钮来重新获取Jane的详细信息,则没有任何反应,因为<App />的状态实际上并未更改。

我知道我可以将控件呈现为QueryDetailView的子控件,并使其触发refetch函数,但这不是我想要的-我想要搜索按钮,每次都会触发查询。

我考虑过的一种可能的解决方法是将App转换为Class组件,并使用搜索按钮的单击处理程序执行类似`this.setState({username:null},()=> {this。 setState({username:searchFieldInput})}),应该触发重新渲染,但这似乎难以置信。

方法2:<ApolloConsumer />并手动触发query()

react-apollo docs on queries建议,要响应用户输入手动触发查询,应使用ApolloConsumer并在点击处理程序中手动调用client.query()

const App = () => {
  const [searchFieldValue, setSearchFieldValue] = useState('');
  const [user, setUser] = useState(null);

  return (
    <ApolloConsumer>
      {client => (
        <Container>
        <Input
          placeholder="Username"
          name="username"
          id="username"
          value={searchFieldValue}
          width={6}
          onChange={(e, { value }) => {
            setSearchFieldValue(value);
          }}
          />
        <Button onClick={async () => { 
          const data = await client.query({
            query: GET_USER,
            variables: {
              username: searchFieldValue
            },
            fetchPolicy='network-only'
          })
          setUser(data.user);
        }>Search</Button>

        {user && <DetailView user={user} setUser={setUser} />
        </Container>
      )}
    </ApolloConsumer>
  )
}

const DetailView = ({user, setUser}) => (
  // .... render the user detail
  // includes a couple of <Mutation /> components that modify the user
  // These need to have the setUser function passed down to them so that they can update the state in <App />
)

有效方法:

单击搜索按钮后,查询将发送到服务器。返回结果,并将其存储在缓存中。但是,在这种情况下,无法使用<DetailView />中的缓存值。结果必须存储在状态中并传递下来。

由于查询是在onClick处理程序中强制执行的,因此我可以反复搜索相同的值,并将查询发送到服务器(由于network-only的提取策略)

什么不起作用:

尽管DetailView中的突变起作用,并且缓存在它们返回时被更新,但UI不会更新,因为它是<App />状态对象的结果。这意味着,为了使UI对突变的结果做出反应,我需要将setUser的功能向下传递给DetailView的所有子代(突变所在的地方),并且在突变的setUser处理程序中调用onCompleted。这应该可以,但是似乎很多不必要的工作。另外,当强制调用loading时,我无法轻易地对errorclient.query()状态做出反应。

我考虑过但尚未尝试过的一件事是命令式和声明式的结合:将<App />包裹在<ApolloConsumer>中,然后在按钮的onClick中手动触发查询具有network-only提取策略的处理程序,并让DetailView组件为同一查询呈现<Query/>组件,但具有cache-only提取策略。同样,这似乎太麻烦了。

就像我说的那样,我确定我缺少非常简单的东西,但是我看不到它是什么。任何帮助将不胜感激。谢谢!

0 个答案:

没有答案