【问题标题】:displaying search query results in all pages在所有页面中显示搜索查询结果
【发布时间】:2020-11-17 20:02:25
【问题描述】:

我使用 unsplash api 制作了一个图像搜索应用程序。该应用程序包含分页,这就是问题所在。当我搜索诸如花之类的东西时,它会在第一页显示花,但是当我转到第二页时,不再有花,而是由 componentDidMount 中的 fetchPhotos 函数生成的默认照片,因为重新渲染我相信。有没有一种方法可以让我在刷新页面之前查看所有页面中的搜索查询结果?

sandbox

App.js

import React, { Component } from "react";
import Pagination from "./Pagination";
import List from "./List";
import "./styles.css";
import axios from "axios";

const LOAD_STATE = {
  SUCCESS: "SUCCESS",
  ERROR: "ERROR",
  LOADING: "LOADING"
};

const appId = "N1ZIgf1m1v9gZJhledpAOTXqS8HqL2DuiEyXZI9Uhsk";
const BASE_URL = "https://api.unsplash.com/photos?page=1";

export default class App extends Component {
  constructor() {
    super();
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.state = {
      photos: [],
      totalPhotos: 0,
      perPage: 5,
      currentPage: 1,
      loadState: LOAD_STATE.LOADING,
      search: ""
    };
  }
  handleChange(e) {
    this.setState({ search: e.target.value });
  }

  handleSubmit() {
    const { search, perPage } = this.state;
    console.log(search);
    const url =
      "https://api.unsplash.com/search/photos?page=1&query=" +
      search +
      "&client_id=" +
      appId;

    const options = {
      params: {
        client_id: appId,
        page: 1,
        per_page: perPage,
        order_by: "popularity"
      }
    };

    this.setState({ loadState: LOAD_STATE.LOADING });
    axios
      .get(url, options)
      .then(response => {
        console.log(typeof response.data);
        let photos;
        try {
          photos = response.data.results;
        } catch {
          photos = [];
        }
        this.setState({
          photos,
          totalPhotos: parseInt(response.headers["x-total"]),
          currentPage: 1,
          loadState: LOAD_STATE.SUCCESS
        });
      })
      .catch(() => {
        this.setState({ loadState: LOAD_STATE.ERROR });
      });
  }

  componentDidMount() {
    this.fetchPhotos(this.state.currentPage);
  }

  fetchPhotos(page) {
    var self = this;
    const { perPage } = this.state;

    const options = {
      params: {
        client_id: appId,
        page: page,
        per_page: perPage,
        order_by: "popularity"
      }
    };

    this.setState({ loadState: LOAD_STATE.LOADING });
    axios
      .get(BASE_URL, options)
      .then(response => {
        self.setState({
          photos: response.data,
          totalPhotos: parseInt(response.headers["x-total"]),
          currentPage: page,
          loadState: LOAD_STATE.SUCCESS
        });
      })
      .catch(() => {
        this.setState({ loadState: LOAD_STATE.ERROR });
      });
  }

  render() {
    return (
      <div className="app">
        <input
          onChange={this.handleChange}
          type="text"
          name="search"
          placeholder="Enter query"
        />
        <button type="submit" onClick={this.handleSubmit} className="button">
          Submit
        </button>
        <Pagination
          current={this.state.currentPage}
          total={this.state.totalPhotos}
          perPage={this.state.perPage}
          onPageChanged={this.fetchPhotos.bind(this)}
        />
        {this.state.loadState === LOAD_STATE.LOADING ? (
          <div className="loader" />
        ) : (
          <List data={this.state.photos} />
        )}
      </div>
    );
  }
}

分页

import React, { Component } from "react";

export default class Pagination extends Component {
  pages() {
    var pages = [];
    for (var i = this.rangeStart(); i <= this.rangeEnd(); i++) {
      pages.push(i);
    }
    return pages;
  }

  rangeStart() {
    var start = this.props.current - this.props.pageRange;
    return start > 0 ? start : 1;
  }

  rangeEnd() {
    var end = this.props.current + this.props.pageRange;
    var totalPages = this.totalPages();
    return end < totalPages ? end : totalPages;
  }

  totalPages() {
    return Math.ceil(this.props.total / this.props.perPage);
  }

  nextPage() {
    return this.props.current + 1;
  }

  prevPage() {
    return this.props.current - 1;
  }

  hasFirst() {
    return this.rangeStart() !== 1;
  }

  hasLast() {
    return this.rangeEnd() < this.totalPages();
  }

  hasPrev() {
    return this.props.current > 1;
  }

  hasNext() {
    return this.props.current < this.totalPages();
  }

  changePage(page) {
    this.props.onPageChanged(page);
  }

  render() {
    return (
      <div className="pagination">
        <div className="pagination__left">
          <a
            href="#"
            className={!this.hasPrev() ? "hidden" : ""}
            onClick={e => this.changePage(this.prevPage())}
          >
            Prev
          </a>
        </div>

        <div className="pagination__mid">
          <ul>
            <li className={!this.hasFirst() ? "hidden" : ""}>
              <a href="#" onClick={e => this.changePage(1)}>
                1
              </a>
            </li>
            <li className={!this.hasFirst() ? "hidden" : ""}>...</li>
            {this.pages().map((page, index) => {
              return (
                <li key={index}>
                  <a
                    href="#"
                    onClick={e => this.changePage(page)}
                    className={this.props.current == page ? "current" : ""}
                  >
                    {page}
                  </a>
                </li>
              );
            })}
            <li className={!this.hasLast() ? "hidden" : ""}>...</li>
            <li className={!this.hasLast() ? "hidden" : ""}>
              <a href="#" onClick={e => this.changePage(this.totalPages())}>
                {this.totalPages()}
              </a>
            </li>
          </ul>
        </div>

        <div className="pagination__right">
          <a
            href="#"
            className={!this.hasNext() ? "hidden" : ""}
            onClick={e => this.changePage(this.nextPage())}
          >
            Next
          </a>
        </div>
      </div>
    );
  }
}

Pagination.defaultProps = {
  pageRange: 2
};

【问题讨论】:

    标签: javascript reactjs pagination


    【解决方案1】:

    您的onPageChanged 具有获取默认照片的功能。您唯一可以根据动态输入/状态获取照片的功能是在您的handleSubmit 中。将handleSubmit 的主体提取到另一个变量中的箭头函数中,并在handleSubmit 中调用这个新提取的函数,同时将这个新函数传递给onPageChanged 属性。 在 handleSubmitonPageChanged 中为您提供相同的 API 调用

    由于您使用的是类组件,因此将handleSubmit 的主体提取到常量中可能不是一个好主意。

    而是将您的 fetchPhotos 方法重命名为 fetchDefaultPhotos 并将您的 handleSubmit 方法重命名为 fetchPhotos 以更好地表示每个方法的作用。

    将新重命名的fetchPhotos 传递给onPageChanged,并在您的componentDidMount 中调用fetchDefaultPhotos。如果仍然需要,您还可以创建一个新的 handleSubmit 方法,该方法仅调用 fetchPhotos

    export default class App extends Component {
      constructor() {
        super();
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
        this.state = {
          photos: [],
          totalPhotos: 0,
          perPage: 5,
          currentPage: 1,
          loadState: LOAD_STATE.LOADING,
          search: ""
        };
      }
    
      handleChange(e) {
        this.setState({ search: e.target.value });
      }
    
      handleSubmit(newPage) {
        this.fetchPhotos(newPage)
      }
    
      fetchPhotos(newPage) {
        const { search, perPage } = this.state;
        console.log(search);
        const url =
          "https://api.unsplash.com/search/photos?page=1&query=" +
          search +
          "&client_id=" +
          appId;
    
        const options = {
          params: {
            client_id: appId,
            page: 1,
            per_page: perPage,
            order_by: "popularity"
          }
        };
    
        this.setState({ loadState: LOAD_STATE.LOADING });
        axios
          .get(url, options)
          .then(response => {
            console.log(typeof response.data);
            let photos;
            try {
              photos = response.data.results;
            } catch {
              photos = [];
            }
            this.setState({
              photos,
              totalPhotos: parseInt(response.headers["x-total"]),
              currentPage: newPage,
              loadState: LOAD_STATE.SUCCESS
            });
          })
          .catch(() => {
            this.setState({ loadState: LOAD_STATE.ERROR });
          });
      }
    
      componentDidMount() {
        this.fetchDefaultPhotos(this.state.currentPage);
      }
    
      fetchDefaultPhotos(page) {
        var self = this;
        const { perPage } = this.state;
    
        const options = {
          params: {
            client_id: appId,
            page: page,
            per_page: perPage,
            order_by: "popularity"
          }
        };
    
        this.setState({ loadState: LOAD_STATE.LOADING });
        axios
          .get(BASE_URL, options)
          .then(response => {
            self.setState({
              photos: response.data,
              totalPhotos: parseInt(response.headers["x-total"]),
              currentPage: page,
              loadState: LOAD_STATE.SUCCESS
            });
          })
          .catch(() => {
            this.setState({ loadState: LOAD_STATE.ERROR });
          });
      }
    }
    

    更新

    这是一个工作沙箱:https://codesandbox.io/s/so-op-sbx-6rnhu

    【讨论】:

    • 你的意思是说,做一些类似const handleSubmit=function(){.....} 的事情,然后稍后在 onPageChanged={fetchPhotos, handleSubmit} 中使用它??
    • 我已经更改了我的沙盒。现在它几乎可以工作了,除了一部分。每当我搜索某些内容时,所有页码都显示在第一页本身,没有上一个和下一个按钮。但是如果我点击第 2 页然后我可以看到上一个和下一个按钮,并且大多数中间页面显示为.... 有没有办法解决它? codesandbox.io/s/stoic-http-0ulby?file=/sandbox.config.json
    • 您在handleSubmit 中的this.setState 现在重命名为fetchOptions 被硬编码为始终将currentPage 设置为1。您应该更改它并改用传递给方法的参数。
    • 它已设置为页面。我在 setState 中没有看到 1
    • 我重命名为 fetchPhotos 的初始 handleSubmit 被硬编码为 1 它是初始 fetchPhotos 现在重命名为 fetchDefaultPhotos 设置为页面。检查正确
    【解决方案2】:

    问题就在这里:

    onPageChanged={this.fetchPhotos.bind(this)}

    因为fetchPhotos 不使用任何类型的查询来获取特定类型的照片,所以当页面更改时,您会得到默认响应。

    您目前有两个功能可用于获取工作方式不同的照片。您需要将您的代码重构为使用来自状态的当前query 的单个照片获取函数。

    更新

    类似这样的:

    fetchPhotos(page=1) {
        var self = this;
        const { search, perPage } = this.state;
        const url = `https://api.unsplash.com/search/photos?page=${page}&client_id=${appId}`
        const url = search ? `${url}&query=search` : url
    
        const options = {
          params: {
            client_id: appId,
            page: page,
            per_page: perPage,
            order_by: "popularity"
          }
        };
    
        this.setState({ loadState: LOAD_STATE.LOADING });
        axios
          .get(url, options)
          .then(response => {
            self.setState({
              photos: response.data,
              totalPhotos: parseInt(response.headers["x-total"]),
              currentPage: page,
              loadState: LOAD_STATE.SUCCESS
            });
          })
          .catch(() => {
            this.setState({ loadState: LOAD_STATE.ERROR });
          });
      }
    

    然后您可以在挂载时调用fetchPhotos 并使用它来代替您的onSubmit,看起来您可以摆脱它。

    <button type="submit" onClick={this.fetchPhotos} className="button">
              Submit
            </button>
    

    【讨论】:

    • 但是api不一样,怎么做单张抓图功能?能给我看看么?我还是一个新开发者
    • 我在这里仍然遇到一些错误。你能看到一次吗codesandbox.io/s/stoic-http-0ulby?file=/src/App.js .. 搜索结果的 setState 是不同的,所以我也在那里创建了一个条件表达式,但我想我在那里遗漏了一些东西
    • 我快到了。但是当我搜索像循环这样的东西时,所有的页码都显示在第一页,没有上一个和下一个按钮。但是当我转到第二页时,会显示上一个和下一个按钮..你能帮我吗?
    • 因此将 .bind 用于 fetchPhotos 会产生问题(如果您查看正在触发的请求)。最简单的解决方法是使用箭头函数:fetchPhotos = (page = 1) =&gt; { ... },然后直接将其连接起来。所以onPageChanged={this.fetchPhotos.bind(this)} 会变成onPageChanged={this.fetchPhotos}
    猜你喜欢
    • 1970-01-01
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-07
    • 2014-02-14
    • 2021-09-21
    • 2010-11-24
    相关资源
    最近更新 更多