Как настроить таргетинг на конкретный элемент после сопоставления и передачи функции onClick в качестве реквизита - PullRequest
1 голос
/ 15 мая 2019

Я столкнулся с такой проблемой, я получил свой массив записей, извлеченный из API, сопоставил его с отдельными элементами и вывел их как отдельные компоненты. У меня есть функция, которая изменяет состояние родительского компонента, передает значение дочернему компоненту, а дочерний компонент должен скрывать / показывать содержимое div после нажатия кнопки.

Конечно. Это работает, но частично - все мои div скрыты / показаны. Я установил определенный ключ для каждого дочернего компонента, но он не работает.

App.js

import React, { Component } from 'react';
import './App.css';

import axios from 'axios';

import countries from '../../countriesList';
import CitySearchForm from './CitySearchForm/CitySearchForm';
import CityOutput from './CityOutput/CityOutput';
import ErrorMessage from './ErrorMessage/ErrorMessage';

class App extends Component {
 state = {
  country: '',
  error: false,
  cities: [],
  infoMessage: '',
  visible: false
 }

getCities = (e) => {
 e.preventDefault();

const countryName = e.target.elements.country.value.charAt(0).toUpperCase() + e.target.elements.country.value.slice(1);

const countryUrl = 'https://api.openaq.org/v1/countries';
const wikiUrl ='https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exintro&explaintext&format=json&category=city&redirects&origin=*&titles=';
const allowedCountries = new RegExp(/spain|germany|poland|france/, 'i');

if (allowedCountries.test(countryName)) {
  axios
  .get(countryUrl)
  .then( response => {
    const country = response.data.results.find(el => el.name === countryName);
    return axios.get(`https://api.openaq.org/v1/cities?country=${country.code}&order_by=count&sort=desc&limit=10`)
  })
  .then( response => {
    const cities = response.data.results.map(record => {
      return { name: record.city };
    });
    cities.forEach(city => {
      axios
      .get(wikiUrl + city.name)
      .then( response => {
        let id;
        for (let key in response.data.query.pages) {
          id = key;
        }
        const description = response.data.query.pages[id].extract;
        this.setState(prevState => ({
          cities: [...prevState.cities, {city: `${city.name}`, description}],
          infoMessage: prevState.infoMessage = ''
        }))
      })
    })
  })
  .catch(error => { 
    console.log('oopsie, something went wrong', error)
  })
} else {
  this.setState(prevState => ({
    infoMessage: prevState.infoMessage = 'This is demo version of our application and is working only for Spain, Poland, Germany and France',
    cities: [...prevState.cities = []]
  }))
 }
}
descriptionTogglerHandler = () => {
 this.setState((prevState) => {
  return {  visible: !prevState.visible};
 });
};

render () {
return (
  <div className="App">
    <ErrorMessage error={this.state.infoMessage}/>
    <div className="form-wrapper">
      <CitySearchForm getCities={this.getCities} getInformation={this.getInformation} countries={countries}/>
    </div>
    {this.state.cities.map(({ city, description }) => (
      <CityOutput
      key={city} 
      city={city}
      description={description}
      show={this.state.visible}
      descriptionToggler={this.descriptionTogglerHandler} />
    ))}
  </div>
  );
 }
}

export default App;

CityOutput.js

import React, { Component } from 'react';

import './CityOutput.css';


class CityOutput extends Component {
 render() {
  const { city, descriptionToggler, description, show } = this.props;
  let descriptionClasses = 'output-record description'
  if (show) {
   descriptionClasses = 'output-record description open';
  }

 return (
  <div className="output">
    <div className="output-record"><b>City:</b> {city}</div>
    <button onClick={descriptionToggler}>Read more</button>
    <div className={descriptionClasses}>{description}</div>
  </div>
  )
 }
};

export default CityOutput;

Ответы [ 2 ]

2 голосов
/ 15 мая 2019

Поместите клавишу visible и функцию переключения в CityOutput вместо того, чтобы иметь ее в родительском

import React, { Component } from "react";

import "./CityOutput.css";

class CityOutput extends Component {
  state = {
    visible: true
  };

  descriptionTogglerHandler = () => {
    this.setState({ visible: !this.state.visible });
  };

  render() {
    const { city, description } = this.props;
    let descriptionClasses = "output-record description";
    if (this.state.visible) {
      descriptionClasses = "output-record description open";
    }

    return (
      <div className="output">
        <div className="output-record">
          <b>City:</b> {city}
        </div>
        <button onClick={() => this.descriptionTogglerHandler()}>Read more</button>
        <div className={descriptionClasses}>{description}</div>
      </div>
    );
  }
}

export default CityOutput;
1 голос
/ 15 мая 2019

Есть два способа, как я бы подошел к этому,

Первый - установить в вашем штате ключевое свойство и проверить и сравнить этот ключ с дочерними ключами, такими как:

 state = {
  country: '',
  error: false,
  cities: [],
  infoMessage: '',
  visible: false.
currKey: 0
 }
descriptionTogglerHandler = (key) => {
 this.setState((prevState) => {
  return {  currKey: key, visible: !prevState.visible};
 });
};
// then in your child component 

class CityOutput extends Component {
 render() {
  const { city, descriptionToggler, description, show, currKey, elKey } = this.props;
  let descriptionClasses = 'output-record description'
  if (show && elKey === currKey) {
   descriptionClasses = 'output-record description open';
  }

 return (
  <div className="output">
    <div className="output-record"><b>City:</b> {city}</div>
    <button onClick={() => descriptionToggler(elKey)}>Read more</button>
    <div className={descriptionClasses}>{description}</div>
  </div>
  )
 }
};


Другой способ - установить изолированное состояние для каждого дочернего компонента

class CityOutput extends Component {
constructor(props) {
 this.state = {
   show: false
}
}

function descriptionToggler() {
const {show} = this.state;
this.setState({
show: !show
})
}
 render() {
  const { city, descriptionToggler, description } = this.props;
  let descriptionClasses = 'output-record description'
  if (this.state.show) {
   descriptionClasses = 'output-record description open';
  }

 return (
  <div className="output">
    <div className="output-record"><b>City:</b> {city}</div>
    <button onClick={descriptionToggler}>Read more</button>
    <div className={descriptionClasses}>{description}</div>
  </div>
  )
 }
};

Надеюсь, это поможет;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...