I'm trying to build a select component using react-select plugin.
In the process of implementing this project, I have some kind of tricky problem with that. Check out my source code here: https://codesandbox.io/s/j148r99695
The problem that I have is I want to fetch all genresList data from the server and mapping them to select component. But somehow or I do wrong something, It's not working. Please see source code above to help me.
I fetch data from Movies component. Its work well and I pass a props to FormFilter component: <FormFilter genresList={this.state.genres} />. And in the FormFilter component, I check this.props.genresList, it's available. But when I'm trying to assign it to FormFilter state and console.log("state", this.state.genres); that. It's empty. Anyone can tell me why?
Default react-select using value and label to display data to select component. But you know some cases we have to custom that. I try it out by using map to transform to other arrays. But It's the best way? How can I custom valueKey and labelKey.
I'm using react-select beta version2.
UPDATE: I was fixed my project. Please check out the link below. Somehow it's not working. I was commend inside source code.
https://codesandbox.io/s/moym59w39p
So to make it works I have changed the FormFilter.js implementation:
import React, { Component } from "react";
import * as Animated from "react-select/lib/animated";
import AsyncSelect from "react-select/lib/Async";
class FormFilter extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: "",
selectedOption: "",
genres: []
};
}
selectGenreHandleChange = newValue => {
const inputValue = newValue.replace(/\W/g, "");
this.setState({ inputValue });
console.log(inputValue);
};
componentDidMount() {
this.genresOption();
}
filterGenres = inputValue => {
const genres = this.genresOption();
//HERE - return the filter
return genres.filter(genre =>
genre.label.toLowerCase().includes(inputValue.toLowerCase())
);
};
promiseOptions = inputValue => {
return new Promise(resolve => { // HERE - you have to return the promise
setTimeout(() => {
resolve(this.filterGenres(inputValue));
}, 1000);
});
};
genresOption() {
const options = [];
const genres = this.props.genresList.genres; //HERE - array is genres in genresList
if (genres && genres instanceof Array) {
genres.map(genre => options.push({ value: genre.id, label: genre.name}));
}
return options;
}
render() {
const { inputValue } = this.state;
if (this.state.genres) console.log("state", this.state.genres);
if (this.props.genresList)
console.log("Movies props", this.props.genresList);
return (
<div className="filter_form">
<span className="search_element full">
<label htmlFor="genres">Genres</label>
<AsyncSelect
className="select genres"
classNamePrefix="tmdb_select"
isMulti
isSearchable="true"
isClearable="true"
cacheOptions
components={Animated}
value={inputValue}
defaultOptions
onInputChange={this.selectGenreHandleChange}
loadOptions={this.promiseOptions}
/>
</span>
</div>
);
}
}
export default FormFilter;
I have write a comment "HERE - something" to let you know what I changed. There are not big problems :)
I did some changed in your FIDDLE and it's works for me
Something like
import React, {Component} from "react";
import { render } from 'react-dom';
import Movies from './Movies';
import "./styles.css";
class App extends Component {
render() {
return (
<div className="App">
<Movies />
</div>
);
}
}
let a = document.getElementById("root");
render(<App />, a);
Related
my application works fine until i refresh or re-render the page. There is multiple possible explanations for this, but I cant seem to work it out. The first problem might be in the constructor, as I am not sure if I'm allowed to do it this way. However I need to use the match.params to get the right product to use on the page.
The error:
index.js:1 Warning: Encountered two children with the same key, `Size-[object Object]`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
at select
at VariantSelector (http://localhost:3000/static/js/main.chunk.js:7811:1)
at div
at ProductPage (http://localhost:3000/static/js/main.chunk.js:6069:5)
at C (http://localhost:3000/static/js/0.chunk.js:77932:37)
at ConnectFunction (http://localhost:3000/static/js/0.chunk.js:74833:75)
at Route (http://localhost:3000/static/js/0.chunk.js:77675:29)
at Switch (http://localhost:3000/static/js/0.chunk.js:77877:29)
at div
at CollectionPage (http://localhost:3000/static/js/main.chunk.js:9200:3)
at ConnectFunction (http://localhost:3000/static/js/0.chunk.js:74833:75)
at Spinner (http://localhost:3000/static/js/main.chunk.js:7939:5)
at Route (http://localhost:3000/static/js/0.chunk.js:77675:29)
at Switch (http://localhost:3000/static/js/0.chunk.js:77877:29)
at div
at ShopPage (http://localhost:3000/static/js/main.chunk.js:10106:1)
at ConnectFunction (http://localhost:3000/static/js/0.chunk.js:74833:75)
at Route (http://localhost:3000/static/js/0.chunk.js:77675:29)
at Switch (http://localhost:3000/static/js/0.chunk.js:77877:29)
at div
at App (http://localhost:3000/static/js/main.chunk.js:803:5)
at ConnectFunction (http://localhost:3000/static/js/0.chunk.js:74833:75)
at Elements (http://localhost:3000/static/js/0.chunk.js:38972:30)
at PersistGate (http://localhost:3000/static/js/0.chunk.js:83105:5)
at Router (http://localhost:3000/static/js/0.chunk.js:77310:30)
at BrowserRouter (http://localhost:3000/static/js/0.chunk.js:76930:35)
at Provider (http://localhost:3000/static/js/0.chunk.js:74546:20)
And on the front-end:
Before refresh:
After refresh:
The error when I try to select an option after the refresh:
The code of the product page:
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { createStructuredSelector } from 'reselect';
import CustomButton from '../custom-button/custom-button.component';
import './product-detail.styles.scss';
import {addCollectionId} from '../../redux/cart/cart.utils';
import VariantSelector from '../variant-selector/VariantSelector';
import { selectCheckout, selectCart } from '../../redux/cart/cart.selectors';
import { client } from '../../shopify/shopify.utils';
// constants
const ONE_SIZE_FITS_MOST = "One Size Fits Most";
class ProductPage extends Component {
constructor(props) {
super(props);
const { match, collection} = this.props;
const { productId } = match.params;
const product = collection.products.find((product) => (product.title.toLowerCase() === productId.toLowerCase()));
let defaultOptionValues = {};
product.options.forEach((selector) => {
defaultOptionValues[selector.name] = selector.values[0].value;
});
this.state = {
selectedOptions: defaultOptionValues,
product: product,
};
this.handleOptionChange = this.handleOptionChange.bind(this);
this.handleQuantityChange = this.handleQuantityChange.bind(this);
this.findImage = this.findImage.bind(this);
}
findImage(images, variantId) {
const primary = images[0];
const image = images.filter(function (image) {
return image.variant_ids.includes(variantId);
})[0];
return (image || primary).src;
}
handleOptionChange(event) {
const target = event.target
let selectedOptions = this.state.selectedOptions;
selectedOptions[target.name] = target.value;
//console.log('selectedVariant', selectedOptions);
const selectedVariant = client.product.helpers.variantForOptions(this.state.product, selectedOptions)
//console.log(selectedVariant);
this.setState({
selectedVariant: selectedVariant,
selectedVariantImage: selectedVariant.attrs.image
});
console.log(this.state)
}
handleQuantityChange(event) {
this.setState({
selectedVariantQuantity: event.target.value
});
}
addVariantToCart(variantId, quantity) {
const lineItemsToAdd = [{variantId, quantity: parseInt(quantity, 10)}]
const checkoutId = this.props.checkout.id
client.checkout.addLineItems(checkoutId, lineItemsToAdd).then(res => {
this.props.dispatch({type: 'ADD_VARIANT_TO_CART', payload: {isCartOpen: true, checkout: res}});
});
}
render() {
let aOptionNames = [];
let variantImage = this.state.selectedVariantImage || this.state.product.images[0]
let variant = this.state.selectedVariant || this.state.product.variants[0]
let variantQuantity = this.state.selectedVariantQuantity || 1
let variantSelectors = this.state.product.options.map((option) => {
aOptionNames.push(option.name);
return (
<VariantSelector
handleOptionChange={this.handleOptionChange}
key={option.id.toString()}
option={option}
/>
);
});
let bShowOneSizeFitsMost = (variantSelectors.length === 1 && aOptionNames[0] === "Title");
return (
<div className="Product">
{this.state.product.images.length ? <img src={variantImage.src} alt={`${this.state.product.title} product shot`}/> : null}
<h5 className="Product__title">{this.state.product.title}</h5>
<p>€{variant.price}</p>
{bShowOneSizeFitsMost ? <h5 className="Product__title">{ONE_SIZE_FITS_MOST}</h5> : variantSelectors}
<label className="Product__option">
Quantity: <input className="form-control" min="1" type="number" defaultValue={variantQuantity} onChange={this.handleQuantityChange}></input>
</label>
<button className="Product__buy button" onClick={() => this.addVariantToCart(variant.id, variantQuantity)}>Add to Cart</button>
</div>
);
}
}
const mapStateToProps = createStructuredSelector({
checkout: selectCheckout
});
export default connect(
mapStateToProps,
)(withRouter(ProductPage));
The code of the variantselector:
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
import React, {Component} from 'react';
class VariantSelector extends Component {
render() {
return (
<select
className="Product__option"
name={this.props.option.name}
key={this.props.option.name}
onChange={this.props.handleOptionChange}
>
{this.props.option.values.map((value) => {
return (
<option value={value} key={`${this.props.option.name}-${value}`}>{`${value}`}</option>
)
})}
</select>
);
}
}
export default VariantSelector;
If I change the content (in the variant selector) of the option from
<option value={value} key={${this.props.option.name}-${value}}>{${value}}</option>
to
<option value={value} key={${this.props.option.name}-${value}}>{${value.value}}</option>,
it solves the issue (visually) in the option selector, but it still errors out when I try to select an other option. So I am thinking it has something to do with passing entire objects here. However, I can't find a way to make it work. Is the problem in the constructor function of the product page or in the combination of the VariantSelector and the handleOptionChange function?
If anyone has seen a similar problem, or knows what is causing it, I would love to hear. Thank you in advance!
I am coding an app in which there is a collection of reviews and a person can respond to a review, but each review can only have one response. So far, I am doing this by rendering a ReviewResponseBox component in my ReviewCardDetails component and passing the review_id as props.
I have implemented the logic so that once there is one ReviewResponse, the form to write another will no longer appear. However, before I was initializing the state in this component with an empty array, so when I refreshed my page the response went away and the form came back up. (This is now commented out)
I am trying to resolve this by persisting my state using React LocalStorage but am having trouble writing my method to do this. Here is what I have so far:
Component that renders ReviewResponseBox and passes review_id as props:
import React from "react";
import './Review.css';
import { useLocation } from "react-router-dom";
import StarRatings from "react-star-ratings";
import ReviewResponseBox from "../ReviewResponse/ReviewResponseBox";
const ReviewCardDetails = () => {
const location = useLocation();
const { review } = location?.state; // ? - optional chaining
console.log("history location details: ", location);
return (
<div key={review.id} className="card-deck">
<div className="card">
<div>
<div className='card-container'>
<h4 className="card-title">{review.place}</h4>
<StarRatings
rating={review.rating}
starRatedColor="gold"
starDimension="20px"
/>
<div className="card-body">{review.content}</div>
<div className="card-footer">
{review.author} - {review.published_at}
</div>
</div>
</div>
</div>
<br></br>
<ReviewResponseBox review_id={review.id}/>
</div>
);
};
export default ReviewCardDetails;
component that I want to keep track of the state so that it can render the form or response:
import React from 'react';
import ReviewResponse from './ReviewResponse';
import ReviewResponseForm from './ReviewResponseForm';
import { reactLocalStorage } from "reactjs-localstorage";
class ReviewResponseBox extends React.Component {
// constructor() {
// super()
// this.state = {
// reviewResponses: []
// };
// }
fetchResponses = () => {
let reviewResponses = [];
localStorage.setResponses
reviewResponses.push(reviewResponse);
}
render () {
const reviewResponses = this.getResponses();
const reviewResponseNodes = <div className="reviewResponse-list">{reviewResponses}</div>;
return(
<div className="reviewResponse-box">
{reviewResponses.length
? (
<>
{reviewResponseNodes}
</>
)
: (
<ReviewResponseForm addResponse={this.addResponse.bind(this)}/>
)}
</div>
);
}
addResponse(review_id, author, body) {
const reviewResponse = {
review_id,
author,
body
};
this.setState({ reviewResponses: this.state.reviewResponses.concat([reviewResponse]) }); // *new array references help React stay fast, so concat works better than push here.
}
getResponses() {
return this.state.reviewResponses.map((reviewResponse) => {
return (
<ReviewResponse
author={reviewResponse.author}
body={reviewResponse.body}
review_id={this.state.review_id} />
);
});
}
}
export default ReviewResponseBox;
Any guidance would be appreciated
You would persist the responses to localStorage when they are updated in state using the componentDidUpdate lifecycle method. Use the componentDidMount lifecycle method to read in the localStorage value and set the local component state, or since reading from localStorage is synchronous directly set the initial state.
I don't think you need a separate package to handle this either, you can use the localStorage API easily.
import React from "react";
import ReviewResponse from "./ReviewResponse";
import ReviewResponseForm from "./ReviewResponseForm";
class ReviewResponseBox extends React.Component {
state = {
reviewResponses: JSON.parse(localStorage.getItem(`reviewResponses-${this.props.review_id}`)) || []
};
storageKey = () => `reviewResponses-${this.props.review_id}`;
componentDidUpdate(prevProps, prevState) {
if (prevState.reviewResponses !== this.state.reviewResponses) {
localStorage.setItem(
`reviewResponses-${this.props.review_id}`,
JSON.stringify(this.state.reviewResponses)
);
}
}
render() {
const reviewResponses = this.getResponses();
const reviewResponseNodes = (
<div className="reviewResponse-list">{reviewResponses}</div>
);
return (
<div className="reviewResponse-box">
{reviewResponses.length ? (
<>{reviewResponseNodes}</>
) : (
<ReviewResponseForm addResponse={this.addResponse.bind(this)} />
)}
</div>
);
}
addResponse(review_id, author, body) {
const reviewResponse = {
review_id,
author,
body
};
this.setState({
reviewResponses: this.state.reviewResponses.concat([reviewResponse])
}); // *new array references help React stay fast, so concat works better than push here.
}
getResponses() {
return this.state.reviewResponses.map((reviewResponse) => {
return (
<ReviewResponse
author={reviewResponse.author}
body={reviewResponse.body}
review_id={this.state.review_id}
/>
);
});
}
}
I want to create a function which iterate over all element with same class and remove a specific class.
It could be done easily using JavaScript.
const boxes = document.querySelectorAll(".box1");
function remove_all_active_list() {
boxes.forEach((element) => element.classList.remove('active'));
}
But how can I do this similar thing is ReactJs. The problem which I am facing is that I can't use document.querySelectorAll(".box1") in React but, I can use React.createRef() but it is not giving me all elements, it's only giving me the last element.
This is my React Code
App.js
import React, { Component } from 'react';
import List from './List';
export class App extends Component {
componentDidMount() {
window.addEventListener('keydown', this.keypressed);
}
keypressed = (e) => {
if (e.keyCode == '38' || e.keyCode == '40') this.remove_all_active_list();
};
remove_all_active_list = () => {
// boxes.forEach((element) => element.classList.remove('active'));
};
divElement = (el) => {
console.log(el);
el.forEach((element) => element.classList.add('active'))
};
render() {
return (
<div className="container0">
<List divElement={this.divElement} />
</div>
);
}
}
export default App;
List.js
import React, { Component } from 'react';
import data from './content/data';
export class List extends Component {
divRef = React.createRef();
componentDidMount() {
this.props.divElement(this.divRef)
}
render() {
let listItem = data.map(({ title, src }, i) => {
return (
<div className="box1" id={i} ref={this.divRef} key={src}>
<img src={src} title={title} align="center" alt={title} />
<span>{title}</span>
</div>
);
});
return <div className="container1">{listItem}</div>;
}
}
export default List;
Please tell me how can I over come this problem.
The short answer
You wouldn't.
Instead you would conditionally add and remove the class to the element, the component, or to the collection.map() inside your React component.
Example
Here's an example that illustrates both:
import styles from './Example.module.css';
const Example = () => {
const myCondition = true;
const myCollection = [1, 2, 3];
return (
<div>
<div className={myCondition ? 'someGlobalClassName' : undefined}>Single element</div>
{myCollection.map((member) => (
<div key={member} className={myCondition ? styles.variant1 : styles.variant2}>
{member}
</div>
))}
</div>
);
};
export default Example;
So in your case:
You could pass active prop to the <ListItem /> component and use props.active as the condition.
Alternatively you could send activeIndex to <List /> component and use index === activeIndex as the condition in your map.
Explanation
Instead of adding or removing classes to a HTMLElement react takes care of rendering and updating the whole element and all its properties (including class - which in react you would write as className).
Without going into shadow dom and why react may be preferable, I'll just try to explain the shift in mindset:
Components do not only describe html elements, but may also contain logic and behaviour. Every time any property changes, at the very least the render method is called again, and the element is replaced by the new element (i.e. before without any class but now with a class).
Now it is much easier to change classes around. All you need to do is change a property or modify the result of a condition (if statement).
So instead of selecting some elements in the dom and applying some logic them, you would not select any element at all; the logic is written right inside the react component, close to the part that does the actual rendering.
Further reading
https://reactjs.org/docs/state-and-lifecycle.html
Please don't hessitate to add a comment if something should be rephrased or added.
pass the ref to the parent div in List component.
...
componentDidMount() {
this.props.divElement(this.divRef.current)
}
...
<div ref={this.divRef} className="container1">{listItem}</div>
then in App
divElement = (el) => {
console.log(el);
el.childNodes.forEach((element) => element.classList.add('active'))
}
hope this will work. here is a simple example
https://codesandbox.io/s/staging-microservice-0574t?file=/src/App.js
App.js
import React, { Component } from "react";
import List from "./List";
import "./styles.css";
export class App extends Component {
state = { element: [] };
ref = React.createRef();
componentDidMount() {
const {
current: { divRef = [] }
} = this.ref;
divRef.forEach((ele) => ele?.classList?.add("active"));
console.log(divRef);
window.addEventListener("keydown", this.keypressed);
}
keypressed = (e) => {
if (e.keyCode == "38" || e.keyCode == "40") this.remove_all_active_list();
};
remove_all_active_list = () => {
const {
current: { divRef = [] }
} = this.ref;
divRef.forEach((ele) => ele?.classList?.remove("active"));
// boxes.forEach((element) => element.classList.remove('active'));
console.log(divRef);
};
render() {
return (
<div className="container0">
<List divElement={this.divElement} ref={this.ref} />
</div>
);
}
}
export default App;
List.js
import React, { Component } from "react";
import data from "./data";
export class List extends Component {
// divRef = React.createRef();
divRef = [];
render() {
let listItem = data.map(({ title, src }, i) => {
return (
<div
className="box1"
key={i}
id={i}
ref={(element) => (this.divRef[i] = element)}
>
<img src={src} title={title} align="center" alt={title} width={100} />
<span>{title}</span>
</div>
);
});
return <div className="container1">{listItem}</div>;
}
}
export default List;
Create ref for List component and access their child elements. When key pressed(up/down arrow) the elements which has classname as 'active' will get removed. reference
I am trying to create a simple Todo List in ReactJS, while the code is working, I'm having trouble understanding a few pieces of code.
My Application consists of 3 components namely:
index.js - The entry point for the application.
TodoList.js - Rendering the forms, button and TodoItem.js component
TodoItem.js - Actual list which maps over the input text and displays the list.
My TodoList.js component:
import React, { Component } from "react";
import TodoItems from "./TodoItems";
class TodoList extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
this.addItem = this.addItem.bind(this);
}
addItem(e) {
if (this._inputElement.value !== "") {
var newItem = {
text: this._inputElement.value,
key: Date.now()
};
this.setState(prevState => {
return {
items: prevState.items.concat(newItem)
};
});
}
//console.log(this._inputElement.value);
this._inputElement.value = "";
e.preventDefault();
}
render() {
return (
<div className="todoListMain">
<div className="header">
<form onSubmit={this.addItem}>
<input
ref={a => (this._inputElement = a)}
placeholder="enter task"
></input>
<button type="submit">add</button>
</form>
</div>
<TodoItems entries={this.state.items} />
</div>
);
}
}
export default TodoList;
The TodoItems.js component:
import React, { Component } from "react";
class TodoItems extends Component {
createTasks(itemmm) {
return <li key={itemmm.key}>{itemmm.text}</li>;
}
render() {
var todoEntries = this.props.entries;
var listItems = this.props.entries.map(this.createTasks);
// console.log(this.props.entries);
return <ul className="theList">{listItems}</ul>;
}
}
export default TodoItems;
What is the problem?
1) I'm having a hard time understanding how return <li key={itemmm.key}>{itemmm.text}</li>;
works, as "itemmm" is a random parameter which I have passed, also how "itemmm.text" correctly displays the text, as per my understanding "itemmm" is a vague object and I have not defined it anywhere.
2) Importance of <TodoItems entries={this.state.items} /> line of code in my "TodoList.js" component. so far I understand it is using props to dynamically enter a value. But how is it tinkering with the TodoItem.js component?
Thank you for reading,sorry if its a too basic question. Any help would be much appreaciated.
1.understanding "itemmm" is a vague object
createTasks(itemmm) {
return <li key={itemmm.key}>{itemmm.text}</li>;
}
Edit: regarding key and text
var newItem = {
text: this._inputElement.value,
key: Date.now()
};
this.setState(prevState => {
return {
items: prevState.items.concat(newItem)
};
});
The logic starts from here.
You get items as [{key:23213123, text:'xyz'}]
var listItems = this.props.entries.map(this.createTasks);
Here itemmm is the parameter and you can define it with any Name.
Items are passed into TodoItems to entries as props.
Abhinav you are passing items as props in entries and when you do map on an array there is a callback function which is createTask.
createTasks(itemmm) {
return <li key={itemmm.key}>{itemmm.text}</li>;
}
the above function is callback of map method of code below
this.props.entries.map(createTasks);
It is the same thing which i specify below.
this.props.entries.map((itemmm)=>{
return <li key={itemmm.key}>{itemmm.text}</li>;
});
I am learning React and I have a solution that requests information through an API, and when there is a response it sets the state, however when rendering my results it only shows the last response on screen,
Even though there are 4, see image below.
App.js
import React from 'react';
import Tiles from './components/Tiles'
import Form from './components/Form'
import WaterData from './components/WaterData'
class App extends React.Component{
state = {
station_name: undefined,
water_value: undefined,
dateTime: undefined
}
getData = async (e) => {
e.preventDefault();
const name = e.target.elements.name.value;
const api_call = await fetch(`https://waterlevel.ie/geojson/latest/`)
.then(response1 => {
response1.json().then(data =>{
Array.from(data.features).forEach(element => {
if(element.properties['station.name'] === name){
this.setState({
station_name: element.properties['station.name'],
water_value: element.properties['value'],
dateTime: element.properties['datetime'],
});
}
})
});
});
}
render(){
return(
<div>
<Tiles />
<Form loadData={this.getData}/>
<WaterData
station_name={this.state.station_name}
water_value={this.state.water_value}
dateTime={this.state.dateTime}
/>
</div>
)
}
}
export default App;
WaterData.js
import React from 'react';
const Weather = (props) => {
console.log(props)
return(
<li>
<p>Location {props.station_name}</p>
<p>Value {props.water_value}</p>
<p>Date Time: {props.dateTime}</p>
</li>
)
}
export default Weather;
Can someone explain to me why the 4 responses do not display?
This happens because you are replacing the values in your state for each part of your data.
You can filter out the element you want in your array using filter.
And then put the whole array into your state only once :
const api_call = await fetch(`https://waterlevel.ie/geojson/latest/`)
.then(response1 => {
response1.json().then(data => {
const features = Array.from(data.features)
.filter(el => el.properties['station.name'] === name);
this.setState({ features });
})
});
But now, to render all of them, you will need to map your state values :
render(){
return(
<div>
<Tiles />
<Form loadData={this.getData}/>
{this.state.features.map(feat => <WaterData
key={/* Find something unique*/}
station_name={feat.properties['station.name']}
water_value={feat.properties['value']}
dateTime={feat.properties['datetime']}
/>)}
</div>
)
}
There's no need to store all the value separately in your state if they are related to each other, it would be fine for your child component though.
To be sure that the state value is always an array, give it an empty array at the start of your class :
state = {
features: []
}