Получать данные из API, когда нажата кнопка поиска формы, и отображать данные на другой странице в React JS - PullRequest
0 голосов
/ 23 января 2019

Я занимаюсь разработкой веб-приложения React JS, в котором у меня есть форма с четырьмя полями выбора (Марка, Модель, Минимальная цена и Максимальная цена) и кнопкой Поиск.Данные для результатов поиска будут получены из API в соответствии с выбором параметров.Я хочу показать эти данные на другой странице карты (путь к странице: / search), когда пользователь нажал на кнопку поиска.Я использую реагирующий маршрутизатор.URL / конечная точка API - https://mysterious -journey-51969.herokuapp.com / api / search-vehicle /? Q = mercedes & m = sprinter & pf = 0 & pt = 100000 , где поле "q" соответствует марке автомобиля,Поле «m» соответствует модели, поле «pf» соответствует минимальной цене, поле «pt» соответствует максимальной цене.Как я могу это сделать?

Вот мой код компонента формы:

import React, { Component } from 'react';
import { Form, FormGroup, Input } from 'reactstrap';
import { veh_data } from '../shared/vehicle_make_and_models';

const defaultValues = [
    { value: 0, text: 0, key: 1 },
    { value: 500, text: 500, key: 2 },
    { value: 1000, text: 1000, key: 3 },
    { value: 1500, text: 1500, key: 4 },
    { value: 2000, text: 2000, key: 5 },
    { value: 2000, text: 2000, key: 6 }
];

const MIN_TITLE = { selected: true, disabled: true, text: 'Min Price' };
const MAX_TITLE = { selected: true, disabled: true, text: 'Max Price' };

class ImgAndForm extends Component {
    constructor(props) {
        super(props);

        this.handleSearch = this.handleSearch.bind(this);
        this.keyToOption = this.keyToOption.bind(this);
        this.renderOptions = this.renderOptions.bind(this);
        this.handleModelChange = this.handleModelChange.bind(this);

        this.state = {
            minData: [MIN_TITLE, ...defaultValues],
            maxData: [MAX_TITLE, ...defaultValues],

            minValue: null,
            maxValue: null,
            modelSelected: null
        };

    }

    renderOptions(data) {
        return data.map(datum => {
            // this allows us to indicate whether we are selecting or disabling
            const selected = datum.selected || false;
            const disabled = datum.disabled || false;

            return (
                <option key={datum.key} value={datum.value} selected={selected} disabled={disabled}>
                    {datum.text}
                </option>
            );
        });
    }

    handleModelChange(event) {
        console.log(event.target.value);
        this.setState({ modelSelected: event.target.value });
    }

    handleSearch(event) {
        alert("Search button clicked");

    }

    keyToOption(key) {
        return key.split("-")
            .map(word => word.slice(0, 1).toUpperCase() + word.slice(1))
            .join(" ");
    }

    handleMinSelect = event => {
        const value = event.target.value;
        const newMaxValues = [];

        defaultValues.forEach(datum => {
            if (datum.value >= Number.parseInt(value, 10)) {
                newMaxValues.push(datum);
            }
        });

        this.setState({
            maxData: [MAX_TITLE, ...newMaxValues],
            minValue: value
        });
    };

    handleMaxSelect = event => {
        const value = event.target.value;
        this.setState({ maxValue: value });
    };

    render() {

        const vehicles = veh_data.reduce((acc, veh, i) => {
            let make = Object.keys(veh)[0],
                vehModels = veh[make];

            return {
                makes: [
                    ...acc.makes,
                    <option key={make + i} value={make}>{this.keyToOption(make)}</option>
                ],
                models: {
                    ...acc.models,
                    [make]: vehModels.map((model, i) => {
                        return (
                            <option key={make + model + i} value={model}>
                                {this.keyToOption(model)}
                            </option>
                        );
                    })
                }
            };
        }, { makes: [], models: [] });

        const selectedModels =
            this.state.modelSelected && this.state.modelSelected.length ? (
                vehicles.models[this.state.modelSelected]
            ) : (
                    <option value="">Model (select make first)</option>
                );

        return (
            <div>
                <header className="headerbg d-flex">
                    <div className="container my-auto">
                        <div className="row">
                            <div className="offset-1 col-10 offset-lg-0 col-lg-4">
                                <div id="search-form-div" className="container">
                                    <div className="row">
                                        <div className="col-12 my-4">
                                            <h3>Search</h3>
                                            <Form onSubmit={this.handleSearch}>
                                                <FormGroup>
                                                    <Input
                                                        onChange={e => this.handleModelChange(e)}
                                                        type="select"
                                                        name="q"
                                                        id="q"
                                                    >
                                                        <option value="">Make</option>
                                                        {vehicles.makes}
                                                    </Input>
                                                </FormGroup>
                                                <FormGroup>
                                                    <Input type="select" name="m" id="m">
                                                        {selectedModels}
                                                    </Input>
                                                </FormGroup>
                                                <FormGroup>
                                                    <Input type="select"
                                                        name="pf"
                                                        id="pf"
                                                        value={this.state.minValue}
                                                        onChange={this.handleMinSelect}>
                                                        {this.renderOptions(this.state.minData)}
                                                    </Input>
                                                </FormGroup>
                                                <FormGroup>
                                                    <Input
                                                        type="select"
                                                        name="pt"
                                                        id="pt"
                                                        value={this.state.maxValue}
                                                        onChange={this.handleMaxSelect}>
                                                        {this.renderOptions(this.state.maxData)}
                                                    </Input>
                                                </FormGroup>
                                                <FormGroup>
                                                    <Input type="submit" name="search" id="search" className="btn btn-primary" value="Search" />
                                                </FormGroup>
                                            </Form>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </header>

            </div>
        );
    }
}

export default ImgAndForm;

Вот мой код компонента результата поиска:

import React, { Component } from 'react';
import Smallheader from './SmallHeader';
import { Card, CardImg, CardTitle, CardSubtitle } from 'reactstrap';

class SearchResult extends Component {
constructor(props) {
    super(props);

    this.state = {
    };
}


render() {

    return (
        <div>
            <Smallheader />
            <div className="my-5">
                <div className="container text-center" id="contactContainer">
                    <div className="row">
                        <div className="col-lg-12 mx-auto">
                            <h2 className="text-center">Search Results</h2>
                            <hr className="my-4 thick-hr" />
                        </div>
                    </div>
                    <div className="row">
                        <div className="col-6 col-lg-3 mt-4">
                            <Card>
                                <a href="#">
                                    <CardImg src="" className="img-fluid" />
                                    <CardTitle>Title Here</CardTitle>
                                    <CardSubtitle>Price Here</CardSubtitle>
                                </a>
                            </Card>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
}
}

export default SearchResult;

Ответы [ 2 ]

0 голосов
/ 23 января 2019

Вот рабочий раствор ...

https://codesandbox.io/s/lrv2w3qxlq?moduleview=1

Я import отредактировал ваш SearchResults компонент и поместил его прямо под ImgAndForm, но вы можете переместить его куда угодно в этой функции рендеринга.

Для этой конкретной ситуации вам потребуется способ визуализации этого на новой «странице», вам потребуется способ управления общим состоянием приложения, например Redux или хотя бы компонентом контейнера, как упомянуто @MikeZinn, но чтобы сделать это правильно потребует столь же значительного объема работы для реализации маршрутизации и реорганизации всей вашей программы. (Если вы хотите, я покажу вам небольшой взлом, чтобы получить тот же результат без этого на данный момент, но я бы посоветовал поискать более постоянное решение.)

Поскольку компонент SearchResults может быть без сохранения состояния, я удалил функцию конструктора, но пока оставляю ее как класс, потому что этот компонент, вероятно, в конечном итоге нуждается в состоянии.

Я добавил библиотеку axios для извлечения данных из API, но подойдет любой другой модуль XHR, уже использованный в вашей программе.

ПРИМЕЧАНИЕ. Поскольку конкретные конечные точки API, которые ваша форма в настоящее время может запрашивать, недоступны, я жестко запрограммировал предоставленный вами пример 'mercedes', но программа будет регистрировать как 'realQuery', так и 'dummyQuery', так что вы видите что он создает правильную структуру запроса для каждого исправления.

Компонент формы

import React, { Component } from "react";
import { Form, FormGroup, Input } from "reactstrap";
// import { veh_data } from '../shared/vehicle_make_and_models';
import SearchResult from "./result";
import axios from "axios";

const veh_data = [
  { "alfa-romeo": ["145", "90", "Alfa 6", "Alfasud"] },
  { "aston-martin": ["15", "2-Litre", "AM Vantage", "Atom", "Cygnet", "DB2"] },
  { audi: ["100", "200", "A1", "A2", "A3", "A4", "A5", "A6", "A7"] }
];
const defaultValues = [
  { value: 0, text: 0, key: 1 },
  { value: 500, text: 500, key: 2 },
  { value: 1000, text: 1000, key: 3 },
  { value: 1500, text: 1500, key: 4 },
  { value: 2000, text: 2000, key: 5 },
  { value: 2000, text: 2000, key: 6 }
];

const MIN_TITLE = { selected: true, disabled: true, text: "Min Price" };
const MAX_TITLE = { selected: true, disabled: true, text: "Max Price" };

class ImgAndForm extends Component {
  constructor(props) {
    super(props);

    this.handleSearch = this.handleSearch.bind(this);
    this.keyToOption = this.keyToOption.bind(this);
    this.renderOptions = this.renderOptions.bind(this);
    this.handleModelChange = this.handleModelChange.bind(this);

    this.state = {
      minData: [MIN_TITLE, ...defaultValues],
      maxData: [MAX_TITLE, ...defaultValues],

      minValue: "",
      maxValue: "",
      modelSelected: "",
      makeSelected: "",
      searchResults: ""
    };
  }

  renderOptions(data) {
    return data.map(datum => {
      // this allows us to indicate whether we are selecting or disabling
      const selected = datum.selected || false;
      const disabled = datum.disabled || false;

      return (
        <option
          key={datum.key}
          value={datum.value}
          selected={selected}
          disabled={disabled}
        >
          {datum.text}
        </option>
      );
    });
  }

  handleModelChange(event) {
    console.log(event.target.value);
    this.setState({ modelSelected: event.target.value });
  }

  handleMakeChange(event) {
    console.log(event.target.value);
    this.setState({ makeSelected: event.target.value });
  }

  async handleSearch(event) {
    event.preventDefault();

    alert("Search button clicked");
    let { makeSelected, modelSelected, minValue, maxValue } = this.state;
    let realQuery =
      "https://mysterious-journey-51969.herokuapp.com/api/search-vehicle/?" +
      `q=${makeSelected.split("-").join("")}` +
      `&m=${modelSelected.split("-").join("")}` +
      `&pf=${minValue}` +
      `&pt=${maxValue}`;
    let dummyQuery =
      "https://mysterious-journey-51969.herokuapp.com/api/search-vehicle/?q=mercedes&m=sprinter&pf=0&pt=100000";
    console.log("realQuery (was not run)", realQuery);
    console.log("dummyQuery (was run)", dummyQuery);
    let res = await axios.get(dummyQuery).catch(err => console.log(err));
    console.log("res", res.data);
    if (res && res.data) {
      this.setState(prevState => {
        return {
          ...prevState,
          searchResults: res.data
        };
      });
    }
  }

  keyToOption(key) {
    return key
      .split("-")
      .map(word => word.slice(0, 1).toUpperCase() + word.slice(1))
      .join(" ");
  }

  handleMinSelect = event => {
    const value = event.target.value;
    const newMaxValues = [];

    defaultValues.forEach(datum => {
      if (datum.value >= Number.parseInt(value, 10)) {
        newMaxValues.push(datum);
      }
    });

    this.setState({
      maxData: [MAX_TITLE, ...newMaxValues],
      minValue: value
    });
  };

  handleMaxSelect = event => {
    const value = event.target.value;
    this.setState({ maxValue: value });
  };

  render() {
    const vehicles = veh_data.reduce(
      (acc, veh, i) => {
        let make = Object.keys(veh)[0],
          vehModels = veh[make];

        return {
          makes: [
            ...acc.makes,
            <option key={make + i} value={make}>
              {this.keyToOption(make)}
            </option>
          ],
          models: {
            ...acc.models,
            [make]: vehModels.map((model, i) => {
              return (
                <option key={make + model + i} value={model}>
                  {this.keyToOption(model)}
                </option>
              );
            })
          }
        };
      },
      { makes: [], models: [] }
    );

    const selectedModels =
      this.state.makeSelected && this.state.makeSelected.length ? (
        vehicles.models[this.state.makeSelected]
      ) : (
        <option value="">Model (select make first)</option>
      );

    return (
      <div>
        <header className="headerbg d-flex">
          <div className="container my-auto">
            <div className="row">
              <div className="offset-1 col-10 offset-lg-0 col-lg-4">
                <div id="search-form-div" className="container">
                  <div className="row">
                    <div className="col-12 my-4">
                      <h3>Search</h3>
                      <Form onSubmit={this.handleSearch}>
                        <FormGroup key={1}>
                          <Input
                            onChange={e => this.handleMakeChange(e)}
                            type="select"
                            name="q"
                            id="q"
                          >
                            <option value="">Make</option>
                            {vehicles.makes}
                          </Input>
                        </FormGroup>
                        <FormGroup key={2}>
                          <Input
                            onChange={e => this.handleModelChange(e)}
                            type="select"
                            name="m"
                            id="m"
                          >
                            {selectedModels}
                          </Input>
                        </FormGroup>
                        <FormGroup key={3}>
                          <Input
                            type="select"
                            name="pf"
                            id="pf"
                            value={this.state.minValue}
                            onChange={this.handleMinSelect}
                          >
                            {this.renderOptions(this.state.minData)}
                          </Input>
                        </FormGroup>
                        <FormGroup key={4}>
                          <Input
                            type="select"
                            name="pt"
                            id="pt"
                            value={this.state.maxValue}
                            onChange={this.handleMaxSelect}
                          >
                            {this.renderOptions(this.state.maxData)}
                          </Input>
                        </FormGroup>
                        <FormGroup key={5}>
                          <Input
                            type="submit"
                            name="search"
                            id="search"
                            className="btn btn-primary"
                            value="Search"
                          />
                        </FormGroup>
                      </Form>
                      <SearchResult results={this.state.searchResults} />
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </header>
      </div>
    );
  }
}

export default ImgAndForm;

Компонент результатов

import React, { Component } from "react";
// import Smallheader from './SmallHeader';
import { Card, CardImg, CardTitle, CardSubtitle } from "reactstrap";

class SearchResult extends Component {
  renderResults() {
    let { results } = this.props;
    console.log("results", results);
    if (results && results.length) {
      return results.map(({ price, text, title, remote_image }, i) => {
        return (
          <Card key={"card-" + i}>
            <a href="#">
              <CardImg src={remote_image} className="img-fluid" />
              <CardTitle>{title}</CardTitle>
              <CardSubtitle>{price}</CardSubtitle>
            </a>
          </Card>
        );
      });
    }
  }

  render() {
    return (
      <div>
        {/* <Smallheader /> */}
        <div className="my-5">
          <div className="container text-center" id="contactContainer">
            <div className="row">
              <div className="col-lg-12 mx-auto">
                <h2 className="text-center">Search Results</h2>
                <hr className="my-4 thick-hr" />
              </div>
            </div>
            <div className="row">
              <div className="col-6 col-lg-3 mt-4">{this.renderResults()}</div>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

export default SearchResult;
0 голосов
/ 23 января 2019

Это именно тот тип проблем, которые Redux решает без использования Redux, вам нужно будет сохранить состояние общего родительского компонента. Например,

class Search extends Component {
  state = {
    searchResult: null,
  };

  handleSearch = searchResult => {
    this.setState({
      searchResult,
    });
  }

  render(){
    const { searchResult, } = this.state;
    if(searchResult === null){
      return (
        <ImgAndForm handleSearch={this.handleSearch} />
      );
    }

    return (
      <SearchResult searchResult={searchResult} />
    );
  }
}

...