I've come to a halt making this covid19 app where I can see a list of countries on the left side of the screen with the option of adding any number of countries to the right side of the screen, which displays more covid data of the added country. I'm also kinda new to React.
Problem is, when I click the add button the added state is updated, and it displays that added country on the right side of the screen. But, when I try adding another country I get an error. I believe the error is somewhere around when I try to setState({ state }) in the addCountry method from within App.js.
In other words, the 'added' state is only letting itself hold no more than one array element. Help much much much appreciated. I posted all the code.
index.js
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import './index.css';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
App.js
import CountryList from "./components/CountryList.js";
import Find from "./components/Find.js";
import Added from "./components/Added.js";
class App extends Component {
constructor() {
super();
this.state = {
countries: [],
inputbox: [],
added: [],
};
}
// Arrow functions capture "this" when they are defined, while standard functions do when they are executed.
// Thus, no need for the bind method. Awesome.
handleChange = (e) =>
this.setState({
inputbox: e.target.value,
});
getCountryData = async (slug) => {
const resp = await fetch(`https://api.covid19api.com/live/country/${slug}`);
var addedData = await resp.json();
// Api returns most days of covid, per country, that it tracks
// Thus, we want the last tracked day of a country
addedData = addedData[addedData.length - 1];
return addedData;
};
// Add a country to the added state
// Call when user clicks button associated with their desired country
addCountry = async (btnId) => {
const { countries, added } = this.state;
var addedData = await this.getCountryData(btnId);
countries.map((country) => {
// If the button ID is equal to the current country in the loops' Slug
if (btnId == country.Slug) {
try {
added.push([
{
addedCountry: addedData.Country,
confirmedTotal: addedData.Confirmed,
deathsTotal: addedData.Deaths,
recoveredTotal: addedData.Recovered,
activeTotal: addedData.Active,
},
]);
// (bug) IT IS PUSHING, BUT ITS NOT SETTING THE STATE!
// ITS ONLY LETTING ME KEEP ONE ITEM IN THE STATE
this.setState({ added });
console.log(added);
} catch (error) {
alert(`Sorry, country data not available for ${country.Country}`);
return;
}
}
});
};
removeCountry = (btnId) => {
const { added } = this.state;
added.map((added, index) => {
//console.log(added[index].addedCountry);
if (btnId == added[index].addedCountry) {
added.splice(index, 1);
this.setState({ added: added });
} else {
console.log("not removed");
return;
}
});
};
// Mount-on lifecycle method
async componentDidMount() {
const resp = await fetch("https://api.covid19api.com/countries");
const countries = await resp.json(); // parsed response
this.setState({ countries }); // set state to parsed response
}
render() {
// Filter out countries depending on what state the inputbox is in
const { countries, inputbox } = this.state;
const filtered = countries.filter((country) =>
country.Country.includes(inputbox)
);
return (
<div className="App Container">
<Find
placeholder="Type to find a country of interest..."
handleChange={this.handleChange}
/>
<div className="row">
<CountryList countries={filtered} addCountry={this.addCountry} />
<Added added={this.state.added} removeCountry={this.removeCountry} />
</div>
</div>
);
}
}
export default App;
Added.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
import AddedCountry from "./AddedCountry.js";
class Added extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="col-md-6">
<Table>
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Country</th>
<th scope="col">Active</th>
<th scope="col">Confirmed Total</th>
<th scope="col">Recovered</th>
<th scope="col">Deaths</th>
<th scope="col">Action</th>
</tr>
</thead>
{this.props.added.map((added, index) => (
<AddedCountry
added={added[index]}
removeCountry={this.props.removeCountry}
/>
))}
</Table>
</div>
);
}
}
export default Added;
AddedCountry.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
class AddedCountry extends Component {
constructor(props) {
super(props);
}
render() {
return (
<tbody>
<tr>
<td></td>
<td>{this.props.added.addedCountry}</td>
<td>{this.props.added.activeTotal}</td>
<td>{this.props.added.confirmedTotal}</td>
<td>{this.props.added.recoveredTotal}</td>
<td>{this.props.added.deathsTotal}</td>
<td>
{
<Button
onClick={() =>
this.props.removeCountry(
document.getElementById(this.props.added.addedCountry).id
)
}
id={this.props.added.addedCountry}
type="submit"
color="danger"
size="sm"
>
Remove
</Button>
}
</td>
</tr>
</tbody>
);
}
}
export default AddedCountry;
CountryList.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
import Country from "./Country.js";
class CountryList extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="col-md-6">
<Table>
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Country</th>
<th scope="col">Actions</th>
</tr>
</thead>
{
// Each country is a component
// Function will display all countries as the Map function loops through them
this.props.countries.map((country) => (
<Country countries={country} addCountry={this.props.addCountry} />
))
}
</Table>
</div>
);
}
}
export default CountryList;
Country.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
class Country extends Component {
constructor(props) {
super(props);
}
render() {
return (
<tbody>
<tr>
<td></td>
<td>{this.props.countries.Country}</td>
<td>
{
<Button
onClick={() =>
this.props.addCountry(
document.getElementById(this.props.countries.Slug).id
)
}
id={this.props.countries.Slug}
type="submit"
color="success"
size="sm"
>
Add
</Button>
}
</td>
</tr>
</tbody>
);
}
}
export default Country;
Find.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
class Find extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="Find container">
<br />
<Form>
<div className="form-row">
<div className="form-group col-md-6">
<h3>Find a Country</h3>
<Input
type="text"
className="form-control"
id="country"
placeholder={this.props.placeholder}
onChange={this.props.handleChange}
></Input>
</div>
</div>
</Form>
</div>
);
}
}
export default Find;
I haven't pored over all that code, but focusing right where you think the issue is it is obvious you are mutating your state object by pushing directly into the added array.
Solution
Don't mutate state!
Since it seems you only want to add a single new "add" and only when the button's btnId matches a country's slug, and the btnId can only ever be a valid value from the mapped countries array, I think this can be greatly simplified.
addCountry = async (btnId) => {
const addedData = await this.getCountryData(btnId);
if (addedData) {
this.setState(prevState => ({
added: prevState.added.concat({ // <-- concat creates a new array reference
addedCountry: addedData.Country,
confirmedTotal: addedData.Confirmed,
deathsTotal: addedData.Deaths,
recoveredTotal: addedData.Recovered,
activeTotal: addedData.Active,
}),
}));
} else {
alert(`Sorry, country data not available for ${country.Country}`);
}
};
Similarly the removeCountry handler is mis-using the array mapping function and mutating the added state. Array.prototype.filter is the idiomatic way to remove an element from an array and return the new array reference.
removeCountry = (btnId) => {
this.setState(prevState => ({
added: prevState.added.filter(el => el.addedCountry !== btnId),
}));
};
Additional Issues & Suggestions
Added.js
If you maintain the added array as a flat array (not an array of arrays) then it's trivial to map the values.
{this.props.added.map((added) => (
<AddedCountry
key={added}
added={added}
removeCountry={this.props.removeCountry}
/>
))}
Country.js & AddedCountry.js
I don't see any reason to query the DOM for the button id when you are literally right there and can enclose the country slug in the onClick callback.
<Button
onClick={() => this.props.addCountry(this.props.countries.Slug)}
id={this.props.countries.Slug}
type="submit"
color="success"
size="sm"
>
Add
</Button>
<Button
onClick={() => this.props.removeCountry(this.props.added.addedCountry)}
id={this.props.added.addedCountry}
type="submit"
color="danger"
size="sm"
>
Remove
</Button>
App.js
This may or may not matter, but it is often the case to do case-insensitive search/filtering of data. This is to ensure something like "France" still matching a user's search input of "france".
const filtered = countries.filter((country) =>
country.Country.toLowerCase().includes(inputbox.toLowerCase())
);
Related
I am new to react and I am having difficulty receiving value from another component. So what I am trying to do is that I am taking in an input value which is the stock ticker from the user in serachBar.js and passing the the ticker value to salesModel.js where the stock data will be displayed in a table format. However, before displaying the data I am sending the value to StockData.js where the API retrives the information based on the user input and sends the data back to the salesModel.js file to be displayed. However, for reason I cant pass the value from the searchBar.js file tosalesModel.jsfile. I have places the code the below for both components and also thank you in advance for any advice I would really appreciate it.
Searchbar.js
class SearchBar extends React.Component{
constructor(props){
super(props);
this.state = {
inputTicker:''
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// handleSubmit (event) {
handleSubmit = event => {
event.preventDefault();
const { inputTicker } = this.state;
console.log("Current value?", inputTicker);
}
handleChange = event => {
// handleChange(event) {
// event.preventDefault();
console.log("Taking in value:",event.target.value);
this.setState({
inputTicker: event.target.value
});
// console.log("State:", JSON.parse(this.state.inputTicker));
}
render() {
return(
<div>
<form onSubmit={this.handleSubmit} >
<div className="search-container">
<span className="search-icon-btn" >
<button className="search-icon-btn"
value="Submit"
type="submit"
>
<i className="fa fa-search" ></i>
</button>
</span>
<div className="search-input">
<input
type="text"
className="search-bar"
placeholder="Search ticker symbol..."
value={this.state.inputTicker}
onChange= {this.handleChange}
/>
</div>
</div>
</form>
</div>
)
}
}
export default SearchBar;
salesModel.js
class salesModel extends Component{
// function salesModel (props) {
constructor(props){
super(props);
this.state = {
ticker:''
}
}
componentDidMount() {
console.log("passed the value:",this.props.inputTicker);
}
render(){
return(
<div className="salesModel" >
<h1>Price to Sales Model (PS)</h1>
<table className="table mt-5">
<tbody>
<tr>
<th>Ticker</th>
<th>Company</th>
<th>Price</th>
<th>PE Ratio</th>
<th>Market Cap</th>
<th>Shares Outstanding</th>
<th>Beta</th>
</tr>
</tbody>
<tbody>
{/* <SearchBar handleSubmit={(input)=> this.setState({ticker: input})}/> */}
{/* <StockData ticker = "AAPL" /> */}
{/* <StockData ticker = {this.props.inputTicker} /> */}
</tbody>
</table>
</div>
);
}
}
export default salesModel;
I have tried calling the <SearchBar handleSubmit={(input)=> this.setState({ticker: input})}/> and setting the value as to ticker but for some reason that didn't work as well. I have tried to use the props.inputTicker from the searchBar.js file but for some reason it would give me an error Uncaught (in promise) SyntaxError: Unexpected token U in JSON at position 0. Honestly I have been stuck at this problem for sometime any help would be greatly appreciative thank you.
So what you need is a parent component that takes care of the state for both of them.
In this example the App.js is the parent and the SearchBar.jsand SalesModal.js are the children
Here is an codesandbox example
// App.js (parent component)
import React, { useState } from "react";
import SearchBar from "./SearchBar";
import SalesModal from "./SalesModal";
const App = () => {
const [searchBarText, setSearchBarText] = useState("");
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<SearchBar
searchBarText={searchBarText}
setSearchBarText={setSearchBarText}
/>
<SalesModal searchBarText={searchBarText} />
</div>
);
};
export default App;
// SearchBar.js (child component)
import React from "react";
const SearchBar = ({ searchBarText, setSearchBarText }) => {
return (
<input
type="text"
value={searchBarText}
onChange={(e) => setSearchBarText(e.target.value)}
/>
);
};
export default SearchBar;
// SalesModal.js (child component)
import React from "react";
const SalesModal = ({ searchBarText }) => {
return <div> searchBar Text: {searchBarText}</div>;
};
export default SalesModal;
I trying to display the list using map function javascript but I am getting error saying "TypeError: Cannot read property 'map' of undefined".
import React, { Component } from 'react'
import constants from './Constants'
import axios from 'axios'
class Home extends React.Component {
constructor(props) {
super(props)
this.state = {
value: 0,
results: {},
}
this.handleClick = this.handleClick.bind(this)
this.input = React.createRef()
}
handleClick = event => {
this.setState({ value: this.input.current.value })
event.preventDefault()
}
componentDidMount() {
console.log('componnet did mount')
const that = this
axios.get('https://reqres.in/api/users').then(function(response) {
that.setResults(response.data)
})
}
setResults(data) {
this.setState({ results: data })
}
render() {
let newvalue = this.state.value
let obj = this.state.results
console.log(obj)
let latestvalue =
constants.MONTHS[newvalue] == null
? 'invalid month'
: constants.MONTHS[newvalue]
return (
<div className="home">
<h1>Welcome to my portfolio website</h1>
{obj.data.map(emp => (
<tr>
<td> </td>
</tr>
))}
Enter Month number <input type="text" ref={this.input} />
<button type="button" onClick={this.handleClick}>
{' '}
submit{' '}
</button>
<p> Feel free to browse around and learn more about me.</p>
Month is {latestvalue}
</div>
)
}
}
export default Home
Need to display all the first names on DOM.
I just need to display the first names in that array of object also recommend me which Javascript function best to use display data.
Try to update following block of lines may help you:
{obj && obj.data && obj.data.map(emp =>
<tr>
<td> {emp.first_name}</td>
</tr>
)}
When initializing the state, you need to describe the full shape of the object for TypeScript to understand it.
results : {
obj: {
data: null
}
}
I am using react/redux to build a simple weather retrieval page from a weather api. My question is about handling conditionals in react with a redux state manager. I have a table that displays weather information when a city is entered. The header for this table is visible before the query is made. I want to add a conditional to replace the whole table with a div that tells the user to search for a city. When a search is made I want to render the table.
I made a conditional inside the render method of the list weather container so if there is no weather data display the div else display the table. My problem is the table never displays only the div.
This confuses me because a search should update the state of the container and re-render it with the data, no? I'm guessing that I need to add this logic in my reducer or state? I'm not sure which to use or where to place the conditional to render a div if there is data or the table if a search has been made and there is data.
// search container
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { term: '' };
this.onInputChange = this.onInputChange.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onInputChange(event) {
this.setState({ term: event.target.value });
}
onFormSubmit(event) {
event.preventDefault();
this.props.fetchWeather(this.state.term);
this.setState({ term : ''});
}
render() {
return (
<form onSubmit={this.onFormSubmit}
className={styles.search}>
<div className={styles.wrap}>
<input value={ this.state.term }
onChange={ this.onInputChange }
placeholder="Enter US cities for a forecast"
className={styles.wrap__input}/>
<button type="submit" className={styles.wrap__btn}>search</button>
</div>
</form>
)
}
}
function MapDispatchToProps(dispatch) {
return bindActionCreators({ fetchWeather }, dispatch);
}
export default connect(null, MapDispatchToProps)(SearchBar);
// weather list container
class ListWeather extends Component {
renderWeather(data){
if(!data) {
let id = Math.round(Math.random() * 100);
return (
<tr key={ id }>
<td className={styles.table__data_error} colspan="5">Please enter a valid US City.</td>
</tr>
)
} else {
const temps = data.list.map(weather => weather.main.temp * 9/5 - 459.67);
const pressures = data.list.map(weather => weather.main.pressure);
const humidities = data.list.map(weather => weather.main.humidity);
const avg = "AVG";
return (
<tr key={ data.city.id }>
<td className={styles.table__data_name}>{ data.city.name }</td>
<td><Chart color="red" data={temps} />{avg} Temperature</td>
<td><Chart color="green" data={humidities} />{avg} Humidity</td>
<td><Chart color="blue" data={pressures} />{avg} Pressure</td>
</tr>
)
}
}
render(){
if(!this.props.weather.data) {
return (
<div className={styles.start}>Enter a US City to get started.</div>
)
} else {
return (
<table className={styles.table}>
<thead className={styles.table__head}>
<tr className={styles.table__row}>
<th className={styles.table__rowhead}>City</th>
<th className={styles.table__rowhead}>Temp</th>
<th className={styles.table__rowhead}>Humidity</th>
<th className={styles.table__rowhead}>Pressure</th>
</tr>
</thead>
<tbody className={styles.table__body}>
{ this.props.weather.map(this.renderWeather)}
</tbody>
</table>
);
}
}
}
function MapStateToProps({ weather }) {
return { weather };
}
export default connect(MapStateToProps)(ListWeather)
// actions / index.js
import axios from 'axios';
const API_KEY='b60aa70986cac3edb4248b5569b74a92';
const ROOT_URL=`http://api.openweathermap.org/data/2.5/forecast?
appid=${API_KEY}`;
export const FETCH_WEATHER = 'FETCH_WEATHER';
export function fetchWeather(city) {
const url = `${ROOT_URL}&q=${city},us`;
const request = axios.get(url);
return {
type: FETCH_WEATHER,
payload: request
};
}
// reducers/reducer_weather.js
import { FETCH_WEATHER } from '../actions/index';
export default function(state=[], action) {
switch (action.type) {
case FETCH_WEATHER:
// return state.concat([ action.payload.data ]);
return [ action.payload.data, ...state ]
default : return state;
}
return state;
}
//reducers/index.js
import { combineReducers } from 'redux';
import WeatherReducer from './reducer_weather';
const rootReducer = combineReducers({
weather: WeatherReducer
});
export default rootReducer;
Change your check to check if this.props.weather has a length > 0. Looks like this.props.weather.data will never exist since this.props.weather is an array:
render(){
if(!this.props.weather.length) {
return (
<div className={styles.start}>Enter a US City to get started.</div>
)
} else {
return (
<table className={styles.table}>
<thead className={styles.table__head}>
<tr className={styles.table__row}>
<th className={styles.table__rowhead}>City</th>
<th className={styles.table__rowhead}>Temp</th>
<th className={styles.table__rowhead}>Humidity</th>
<th className={styles.table__rowhead}>Pressure</th>
</tr>
</thead>
<tbody className={styles.table__body}>
{ this.props.weather.map(this.renderWeather)}
</tbody>
</table>
);
}
}
import React, { Component } from 'react';
let _ = require('lodash');
import {bindActionCreators} from "redux";
import {connect} from 'react-redux';
import {fetchedZonesEdit} from '../../actions/';
class InfoRow extends Component {
constructor(props){
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
render() {
return (
<tr>
<td>
{this.props.zone}
</td>
<td>{this.props.zoneValue}
<input type="text"
className="form-control"
defaultValue={this.props.zoneValue}
value={this.props.name}
name={this.props.zone}
onChange={this.handleInputChange}
/>
</td>
</tr>
)
}
}
class ZoneDetailsEdit extends Component {
render() {
const rows = [];
let a = this.props.ezn;
Object.keys(this.props.ezn).map((keyName, keyIndex) =>{
return rows.push(<InfoRow zone={keyName} zoneValue={a[keyName].toString()} key={keyIndex}/>)
});
return (
<div className="col-md-6">
<div className="">
<table className="table table-clear">
<tbody>
{rows}
</tbody>
</table>
</div>
<div className="row px-1" >
<div className="px-2">
<button className="btn btn-sm btn-info">Save</button>
</div></div>
</div>
)
}
}
class ZoneDetailEditComponent extends Component {
componentWillMount = () => {
this.props.fetchedZonesEdit(this.props.location.query.id);
};
render() {
return (
<div className="container px-3 mr-3">
<div className="row">
<div className="col-md-6 col-md-offset-3"><h1>Edit Tag Information</h1></div>
</div>
<br/>
<br/>
{ this.props.ezn != null?
<div>
<ZoneDetailsEdit ezn={this.props.ezn}/>
</div> :
<center><br /><h1><img src={'img/avatars/default.gif'} alt="Loading"/><br />Loading</h1></center>
}
</div>
)
}
}
function mapStateToProps(state) {
return {
ezn: state.zones
}
}
function matchDispatchToProps(dispatch){
return bindActionCreators({fetchedZonesEdit: fetchedZonesEdit}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(ZoneDetailEditComponent);
This is the snippet i provided
what I'm doing right now is i had fetched the data from the api and was shown in the input fields but the issue is data is fetched correctly but when i print outside input fields it works fine but when i enter in input fields as default value it doesn't works
screenshot also provided
when opened the page default values are set of previous page but when it is refreshed it works fine
defaultValue should be used only on Uncontrolled components.
Instead of using defaultValue, assign a default value to the name in your store.
Also, don't use this.setState if you are using redux. You are assigning the new value to this.state.zone which you never read.
Since you are using a controlled component you need not use defaultValue, assigning the props passed down to the value will suffice.
Also using redux, a better practice is to store the UI state in localState and all others in the redux store.
To do that you need to dispatch an action that updates the corresponsing reducer after passing the values to the topmost parent
Also you are not passing any prop as name to the InfoRow component and since defaultValue is rendered only at the time of create, you do not see the update.
You code must look something like
import React, { Component } from 'react';
let _ = require('lodash');
import {bindActionCreators} from "redux";
import {connect} from 'react-redux';
import {fetchedZonesEdit} from '../../actions/';
class InfoRow extends Component {
constructor(props){
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
this.props.handleChange(this.props.zone, event.target.value);
}
render() {
return (
<tr>
<td>
{this.props.zone}
</td>
<td>{this.props.zoneValue}
<input type="text"
className="form-control"
value={this.props.zoneValue}
name={this.props.zone}
onChange={this.handleInputChange}
/>
</td>
</tr>
)
}
}
class ZoneDetailsEdit extends Component {
handleChange(zone, value) {
//pass it to the parent and then fire an action from there to update this value in the store
}
render() {
const rows = [];
let a = this.props.ezn;
Object.keys(this.props.ezn).map((keyName, keyIndex) =>{
return rows.push(<InfoRow zone={keyName} zoneValue={a[keyName].toString()} handleChange={()=> this.handleChange}key={keyIndex}/>)
});
return (
<div className="col-md-6">
<div className="">
<table className="table table-clear">
<tbody>
{rows}
</tbody>
</table>
</div>
<div className="row px-1" >
<div className="px-2">
<button className="btn btn-sm btn-info">Save</button>
</div></div>
</div>
)
}
}
Also there is no need for you to bind the lifeCycle function with arrows
I have created dynamically generate input text-fields but unable to find a way to read and get the values and stored it to an array. please find the code below
i have separate component for add new input field rows called IncrementTableRow
import React, {PropTypes} from 'react';
class IncrementTableRow extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<tr>
<th scope="row">{this.props.index}</th>
<td><input type="text" className="form-control" ref={"firstValue"+this.props.index} placeholder=""/></td>
<td><input type="text" className="form-control" ref={"secondValue"+this.props.index} placeholder=""/></td>
</tr>
);
}
}
export default IncrementTableRow;
also, i have main component to call IncrementTableRow below is the calling line.
export default class SuggestInterestProductDetails extends Component {
constructor(props) {
super(props);
this.state = {
rows: []
};
this.AddRow = this.AddRow.bind(this);
}
AddRow() {
this.setState({
rows: [{val: 5}, ...this.state.rows]
});
}
render() {
let rows = this.state.rows.map(row => {
return <Row />
});
return (
<div>
<button onClick={this.AddRow}>Add Row</button>
<table>
{rows}
</table>
</div>
);
}
}
i need to read each and every generated text field values and stored it to an array
your code example seems incomplete - you dont even add the values to your rows
so here only a short answer:
check react refs
https://facebook.github.io/react/docs/more-about-refs.html
you can add a ref to each row in your
let rows = this.state.rows.map(row => {
return <Row />
});
maybe an even better solution would be to add an onChange listener to your rows and update the state of your parrent component
let rows = this.state.rows.map((row,i) => {
return <Row ref={'row-'+i} onChange={(event) => this.myListener(event,i)} />
});