映射和传递 onClick 函数作为道具后如何定位特定元素

Posted

技术标签:

【中文标题】映射和传递 onClick 函数作为道具后如何定位特定元素【英文标题】:How to target specific element after mapping and passing onClick function as props 【发布时间】:2019-10-02 22:59:12 【问题描述】:

我正面临这样的问题,我从 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;

【问题讨论】:

常见错误——一个数组的值 【参考方案1】:

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;

【讨论】:

在你的例子中 onClick=() =&gt; this.descriptionToggler() 不应该是 onClick=this. descriptionTogglerHandler 吗?看起来你有不同的函数名称 @Ted 我在使用onClick=this.descriptionTogglerHandler 时遇到了问题,因为函数descriptionTogglerHandler 被立即调用,但没有针对这个特定示例进行测试 这不应该是一个问题 - 但我的意思更多的是名称不匹配的问题:descriptionTogglerHandler vs this.descriptionToggler【参考方案2】:

有两种方法可以解决这个问题,

第一个是在您的状态中设置一个键属性,并检查该键并将其与子键进行比较,例如:

 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>
  )
 
;

我希望这会有所帮助;)

【讨论】:

我非常感谢这个答案,因为它涵盖了 2 个场景

以上是关于映射和传递 onClick 函数作为道具后如何定位特定元素的主要内容,如果未能解决你的问题,请参考以下文章

如何将函数作为道具传递给子组件并在Vue中从那里调用它?

如何通过链接传递道具

Vue.js。如何从字符串(道具)制作函数?

在 onClick 道具中使用联合类型

作为道具传递的函数不是函数

将嵌套对象作为道具映射到自定义组件