Autocomplete với React, Apollo, GraphQL

3 min read

TL;DR: Thay vì dùng component Query của apollo-client, sử dụng ApolloConsumer và truy cập trực tiếp đến client qua render prop giúp chúng ta viết một component AutoComplete ngắn gọn và dễ đọc hơn

1. Tạo AutoComplete để tìm kiếm và hiển thị người dùng

Gần đây mình muốn xây dựng tính năng tìm kiếm người dùng trong một dự án. Khi người dùng nhập vào ô Input, danh sách gợi ý sẽ hiển thị. Dữ liệu người dùng được lấy từ endpoint GraphQL (Node.js) bằng Apollo Client. Ban đầu hơi khó, nhưng cách mình dùng cuối cùng như sau .

2. So sánh giữa QueryApolloConsumer

Trước tiên, trong phương thức render() bên trong lớp React.Component của tôi, tôi sử dụng ApolloConsumer thay vì Query component từ thư viện react-apollo. Tài liệu hướng dẫn có nói rằng nên ưu tiên sử dụng Query, nhưng trong trường hợp của tôi thì điều đó lại không đúng. Hãy xem đoạn mã sau:

render() {

 const { dataSource, value } = this.state;

 return (

  <ApolloConsumer>

   {client => (

    <div>

     <AutoComplete

      onSelect={this.onSelect}

      onSearch={value => {

       this.handleSearch({ value, client });

      }}

      placeholder=”Enter Opponent Name”

      value={value}

     >

      {dataSource.map((user, index) => {

       return (

        <AutoComplete.Option key={index}>

         {user.name}

        </AutoComplete.Option>

       );

      })}

     </AutoComplete>

    </div>

   )}

  </ApolloConsumer>

 );

}

Chúng ta thêm component AutoComplete, component này sẽ kích hoạt hàm được chỉ định trong onSearch mỗi khi giá trị nhập vào thay đổi. Đồng thời, chúng ta đặt giá trị (value) của nó dựa trên state của component. Sau đó, chúng ta render từng người dùng gợi ý hiện có trong mảng dataSource từ state.

Nếu chúng ta sử dụng Query component thay cho cách thiết lập này, có thể chúng ta sẽ muốn cập nhật state mỗi khi nhận được phản hồi từ server. Nhưng: Việc cập nhật state bên trong render() là không hoạt động.

Có thể có một cách khác, đó là truyền dữ liệu trực tiếp vào prop dataSource của AutoComplete thông qua data được cung cấp bởi render prop của Query. Tuy nhiên, theo quan điểm của tôi, cách tiếp cận ở trên có vẻ dễ đọc và dễ bảo trì hơn. Giờ hãy cùng tìm hiểu sâu hơn vào bên trong phương thức handleSearch.

3. Cách xử lý truy vấn

Như đã thấy ở trên, chúng ta truyền một đối tượng chứa clientvalue làm đối số cho phương thức handleSearch. Dưới đây là hình thức của nó:

handleSearch = async ({ value, client }) => {

  this.setState({

    value

  });

  if (value.length > 1) {

    const { data } = await client.query({

      query: USERS_QUERY,

      variables: { query: value, boardId: this.props.boardId }

    });

    this.setState({

      dataSource: data ? data.users : []

    });

  }

};

Trước hết, chúng ta muốn cập nhật giá trị trong state để phản ánh đầu vào của người dùng. Bằng cách này, nội dung nhập vào sẽ được hiển thị trong component AutoComplete.

Tiếp theo, chúng ta truy vấn dữ liệu từ client và định nghĩa giá trị vừa nhập (cùng với boardId, một giá trị cụ thể trong ví dụ của tôi) làm biến truy vấn cho USERS_QUERY.

Bằng cách sử dụng async/await, chúng ta cập nhật dataSource trong state để phản ánh danh sách người dùng mới được tìm thấy. Tôi bọc các câu lệnh này trong một câu điều kiện if để chỉ truy vấn dữ liệu khi người dùng nhập ít nhất 2 ký tự, nhằm giảm lưu lượng và tải cho backend.

4. Tổng kết

Còn nhiều việc khác, ví dụ xử lý khi người dùng chọn một item trong danh sách gợi ý, nhưng chúng phụ thuộc mạnh vào từng dự án nên tác giả không đi sâu. Hy vọng bài viết ngắn này đã giúp bạn, hẹn gặp lại lần sau!

Tham khảo thêm:

Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *