I'm relatively new to React and Typescript.
I used a map function to create a list, li, in typescript. I tried passing a parameter to a function with info from the mapped object, but it doesn't seem to happen. it seems like the same argument is mapped to all the elements, like so:
state = {
groups: new Array<IGroups>(),
showGroup: false,
groupId: 0
};
showMembers(groupId: number) {
this.setState({ showGroup: true, groupId: groupId });
}
render() {
return (
<div>
<ul>
{this.state.groups.map(group => <li>{group.name} from {group.cityName} <button onClick={() => this.showMembers(group.groupId)}>For members</button></li>)}
{this.state.showGroup && <GroupMembers groupId={this.state.groupId}></GroupMembers>}
</ul>
</div>
)
}
the same "groupId" is being passed to the component every time.
what am I doing wrong?
Related
I got the error when I tried to get some datas from api.
Then I searched how to solve this error but I couldn't solve. I want to solove this error.
I don't know Why do I get this error.
What should I fix my code ??
Please tell me any ideas.
thank you for reading !
this is my error.
TypeError: Cannot read property 'map' of undefined
and message image.
this is my code.
import React,{ Component } from 'react';
class Exchange extends Component {
constructor(props){
super(props);
this.state = {
isLoaded: false,
items: [],
}
}
componentDidMount(){
fetch('https://api.exchangeratesapi.io/latest')
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
items: json.items,
})
})
}
render(){
var { items,isLoaded } = this.state;
if(!isLoaded){
return <div>...Loading</div>;
}else{
return (
<div>
<ul>
{items.map(item =>(
<li key={item.rates}>{item.CAD}</li>
))}
</ul>
</div>
)
}
}
}
export default Exchange;
and this is using api.
I think the way you access the data from api is wrong it should be
this.setState({
isLoaded: true,
items: json.rates //json.rates not json.items
});
and when rendering it, map expecting a array so you have do something like this
<ul>
{Object.keys(items).map(key => (
<li key={key}>{key} - {items[key]}</li>
))}
</ul>
Demo
There is no attribute items in the json response. That's why items is undefined after following statement : items: json.items.
Also, map() is only applicable to array. Response from the API you are calling is not Array.
You can modify code something like
this.setState({
isLoaded: true,
items: json,
})
And
<ul>
<li>{items.rates.CAD}</li>
</ul>
To change the state of the array this.setState({ items: [...this.state.items, itemsArr] })
If the your response of API is array you can follow the above way.
But in your API response it seems like object, to push object into an array gothrough the link
The code is working with the property 'name', names appear correctly on the map.
I wanted to enrich the json file with datas coming from my mysql database (like, add the name of countries in french or spanish for example).
I added a state 'countries' which will be initialized with json file converted in object. I fetch data from my sql database and then I set the state 'countries' with data I wanted to add.
Here is the code :
import React, { Component } from "react"
import {
ComposableMap,
ZoomableGroup,
Geographies,
Geography,
} from "react-simple-maps"
import ReactTooltip from "react-tooltip"
import jsonWorldMap from "./maps/world-50m.json"
const wrapperStyles = {
width: "100%",
height: "100%",
backgroundColor: "#0565A1"
}
class WorldMap extends Component {
constructor(){
super()
this.state = {
zoom: 1,
color: "#39464E",
countries: jsonWorldMap
}
}
componentDidMount() {
//get all countries in db
fetch('http://localhost:3001/countries')
.then(res => res.json())
.then(body =>
body.data.forEach(function(elementSql){
jsonWorldMap.objects.units.geometries.forEach(function(elementJson){
if(elementSql.alpha3 == elementJson.id)
{
elementJson.properties.nameFr = elementSql.name_fr;
}
})
})
)
this.setState({ countries: jsonWorldMap }, () => console.log(this.state.countries))
}
render() {
return (
<div style={wrapperStyles}>
<ComposableMap>
<ZoomableGroup center={[0,20]}>
<Geographies geography={this.state.countries}>
{(geographies, projection) => geographies.map((geography, i) => geography.id !== "ATA" && (
<Geography
className="Geography"
key={i}
data-tip={geography.properties.nameFr}
geography={geography}
projection={projection}
/>
))}
</Geographies>
</ZoomableGroup>
</ComposableMap>
<ReactTooltip />
</div>
)
}
}
export default WorldMap
So you can see that I added a component to have a console.log at the end of the component. See what console.log gives :
So you can see that the property 'nameFr' is present in the state object 'countries'. But, If I try to display it as tooltip, it doesn't work. And it works perfectly with property 'name' (in data-tip)
If data-tip={geography.properties.name} works fine but data-tip={geography.properties.nameFr} does not, then it seems that the problem is with state.
See your componentDidMount method. You are updating state with jsonWorldMap at the end of this method.
But as fetch is async , at that moment jsonWorldMap may not be updated yet. So I think you should move that line inside fetch. please see below:
componentDidMount() {
const _this = this; // hold this inside _this
//get all countries in db
fetch('http://localhost:3001/countries')
.then(res => res.json())
.then(body => {
body.data.forEach(function(elementSql){
jsonWorldMap.objects.units.geometries.forEach(function(elementJson){
if(elementSql.alpha3 == elementJson.id)
{
elementJson.properties.nameFr = elementSql.name_fr;
}
})
});
_this.setState({ countries: jsonWorldMap }, () => console.log(this.state.countries)); //making sure setting updated jsonWorldMap to state
}
)
}
hope it helps.
thanks
Wrap Geography with an element that uses data-tip as a props.
<div data-tip={geography.properties.nameFr}>
<Geography ... />
</div>
In order to <Geography data-tip={props.nameFr}/> work, Geography component need to use the data-tip property internaly, something like:
function Geography(props) {
return <h1 data-tip={props['data-tip']}>I'm a map</h1>;
}
To solve your problem you need to attach data-tip property to Geography wrapper, for example:
function Geography(props) {
return <h1>I'm a map</h1>;
}
function ComponentWithTooltip({ props }) {
return (
<div data-tip="nameFr">
<Geography />
</div>
);
}
function App() {
return (
<>
<Geography data-tip="hello-world" /> // Your way, won't work
<ComponentWithTooltip /> // Work
<div data-tip="nameFr2"> // Work
<Geography />
</div>
// Works with div wrapper, without won't work.
{geographies.map((geofraphy, i) => (
<div key={i} data-tip={geofraphy.properties.nameFr}>
<Geography />
</div>
))}
<ReactTooltip />
</>
);
}
Check out the demo with all use cases:
I'm learning React on my own. I have a couple of questions. I'm trying to fetch data to another component. I've tried to setState after mapping JSON data. But it was showing an error “setState is not a function”.
How can I setState after mapping JSON data?
// Fetching data and passing JSON values to TabBarMenu component
export default class App extends Component {
state = {
temp: [],
description: [],
time: []
};
_getFiveWeather = (lat, lng) => {
fetch(`http://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lng}&APPID=${API_KEY}`)
.then(response => response.json())
.then(json => json.list.map(function(item) {
return (
<TabBarMenu key={item.dt_txt} time={item.dt_txt} description={item.weather.description} />
);
})
}}
In TabBarMenu.js, I'm trying to pass the props (time, description) to the _FirstRoute. When I did console.log(this.props), it shows empty array.
How can I grab the props value from the constructor?
I really appreciate your help.
import React, { Component } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { TabViewAnimated, TabBar, SceneMap } from 'react-native-tab-view';
export default class TabBarMenu extends Component {
constructor(props) {
super(props);
console.log(this.props) // showing empty array
this.state = {
index: 0,
routes: [
{ key: '1', title: 'Weather' },
{ key: '2', title: 'News' },
{ key: '3', title: 'Photos' },
],
};
}
_FirstRoute = this.props => (
<View style={styles.container}>
<Text style={styles.textStyle}>1</Text>
<Text style={styles.textStyle}>{this.props.description}</Text>
<Text style={styles.textStyle}>{this.props.time}</Text>
<Text style={styles.textStyle}>{this.props.temp}</Text>
</View>
)...
Your _getFiveWeather definition is essentially useless because after fetching data, you are not really doing anything with the mapped components. You just do the map operation and then that's it -- nothing else is happening.
That method that fetches data should be concerned only with that -- fetching data; it should have nothing to do with generating components. You should set the state with the new data after fetching it. Something like this.setState({ ... }) after successful fetching the info.
Then, in the render() method, you verify if there is anything to render. That is, if there is any weather information in your component state properties, you do some sort of mapping to dynamically create the components you need to render.
Nowhere did I see the render() method in your code, so I think you should focus on learning the basics. Start with render(). Don't try to do too many things at once.
How to get multiple checkboxes value ? Ref not working in material ui checkbox, no idea why.
<Checkbox key={i} label={catagory.name} ref="categories" value={catagory_name} name="category" />
for example : example
Without material-ui you can get the value by ref, but with material-ui it require another method to get checkbox value.
I get the data from API, so it will add more from time to time. How to get the value? What function I should write? Anyone know ?
You can use build-in Material UI checkbox function - onChange. It will return the specified category and it's value.
app.js
class App extends Component {
result = new Set();
handleCheckbox(event, isChecked, value) {
console.log(isChecked, value);
this.res.add(value);
if (this.res.size === 3) console.log(this.res);
}
labelList = [{id: 1, category: 'a'}, {id: 2, category: 'b'}, {id: 3, category: 'c'}]; // your data
render() {
return (
<div className="App">
{this.labelList.map(element => (
<CheckboxField
key={element.id}
label={element.category}
category={element.category}
onChange={this.handleCheckbox}
/>
))}
</div>
)
}
}
Checkbox.js
export class CheckboxField extends React.PureComponent {
handleCheck = (event, isInputChecked) => {
this.props.onChange(event, isInputChecked, this.props.category);
};
render() {
return (
<Checkbox
label={this.props.category}
iconStyle={{fill: '#000'}}
value={this.props.category}
onCheck={this.handleCheck}
/>
)}
}
Hey there :) I got following issue by adding a filter Modal to my SearchView
I constructed a SearchPage where several events can be listed. This all workes pretty fine. Now i am trying to add filter to my SearchPage. If i set the filter manually it works pretty fine -> Now my issue:
If i try to change the switch value of the Switch, it set´s back to the root because the state for the value is not set
Steps i did explained:
I am trying to open a Modal View where all my filter are listed and where i can set true/false by using a Switch. My idea was to fetch all filter Settings by creating a JSON for it:
module.exports = {
"filter":
{
"track": [
{
"id": 1,
"description": "IoT & Living tomorrow"
},
{
"id": 2,
"description": "Smart & Digital Retail"
},
{
"id": 3,
"description": "Startups, Digital Culture & Collaboration"
}
]
}
}
The JSON above is just for expample - Normally its much larger and has more topics than just track
Now i import the JSON and save it at the var filter. I checked the data is in the right format here -> filter.track -> All my JSON Objects
Now i created a my class with the filter Modal
import React, {Component} from 'react';
import {
ListView,
Modal,
StatusBar,
StyleSheet,
Text,
TouchableOpacity,
View,
Switch
} from 'react-native';
var filter = require('../JSON/filter');
class PopoverFilter extends Component {
constructor(props) {
super();
// ds for the menu entries
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
eventTracks: ds.cloneWithRows(filter.filter.track)
}
this.show = this.show.bind(this);
}
render() {
return(
<Modal>
<ListView
style={styles.mainView}
renderRow={this.renderMenuEntries.bind(this)}
dataSource={this.state.eventTracks}/>
</Modal>
);
}
renderMenuEntries(entry) {
var switchState = entry.description;
return(
<View style={styles.switchView}>
<Text style={[styleHelper.fonts.titleSize, styles.text]}>{entry.description}</Text>
<Switch onValueChange={(value) => this.switchChanged(switchState, value)}
value={this.state.switchState}/>
</View>
);
}
switchChanged(field, value) {
var obj = {};
obj[field] = value;
this.setState(obj);
}
}
var styles = StyleSheet.create({
});
module.exports = PopoverFilter;
Please ignore the missing Style and also there are more Objects in the Modal but its not important for this case.
Most important is that i try to render the every Switch by the renderMenuEntries method and i give them all entries -> The works just the Switch is not set right. As far as i try to change the value of the switch it is instant go back to its root. And no state is set.
Maybe my solution is not possible and i have to make every state static - but this solution would be very good in case that i could set dynamic filter later without changing the whole code
The scenario you describe is possible. There were a number of issues I encountered with your code:
In renderMenuEntries the value you were assigning to the <Switch /> component was the description of the data item, instead of the expected boolean that the <Switch /> component value expects. Further, this value was also referencing a property of this.state that didn't exist.
The switchChanged function was also just updating the component state using the data item's description
Using your code sample provided I created a new class from scratch named PopoverFilter. Instead of requiring the filter data within this component, it expects the data to come in via a component prop named filterData. This will promote reusability of the component to accept different datasets.
The code is heavily commented to help explain the concepts demonstrated. Here's the PopoverFilter class:
import React from 'react';
import {
ListView,
Modal,
Switch,
Text,
TouchableOpacity,
View
} from 'react-native';
export default class PopoverFilter extends React.Component {
constructor (props) {
super(props);
// bind relevant handlers up front in the constructor
this.renderRow = this.renderRow.bind(this);
this.onPress = this.onPress.bind(this);
// process the incoming filter data to add a 'selected' property
// used to manage the selected state of its companion switch
this._filterData = this.processFilterData(this.props.filterData);
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
this.state = {
filterDataSource: ds.cloneWithRows(this._filterData)
}
}
processFilterData (filterData) {
// don't mutate the filterData prop coming in
// use map to create a new array and use Object.assign to make
// new object instances with a new property named 'selected' initialized
// with a value of false
return filterData.map((item) => Object.assign({}, item, { selected: false }));
}
switchChanged (rowId, isSelected) {
const index = +rowId; // rowId comes in as a string so coerce to a number
const data = this._filterData;
// don't mutate this._filterData
// instead create a new array and new object instance
this._filterData = [
...data.slice(0, index), // take everything before the target index
Object.assign({}, data[index], { selected: isSelected }), // create a new object instance with updated selected property
...data.slice(index + 1) // take everything after the selected index
];
// update the listview datasource with the new data
this.setState({
filterDataSource: this.state.filterDataSource.cloneWithRows(this._filterData)
});
}
renderRow (item, sectionId, rowId) {
return(
<View>
<Text>{item.description}</Text>
<Switch
onValueChange={(value) => this.switchChanged(rowId, value)}
value={item.selected}
/>
</View>
);
}
// just a test function used to dump the current state of the _filterData
// to the console
onPress () {
console.log('data', this._filterData);
}
render () {
return (
<Modal>
<ListView
renderRow={this.renderRow}
dataSource={this.state.filterDataSource}
/>
<TouchableOpacity onPress={this.onPress}>
<Text>Get Filter Data</Text>
</TouchableOpacity>
</Modal>
);
}
}
Note this PopoverFilter class also renders a button that when pressed will dump out the current state of the data to the console so you can view it's current form.
Here's an example of how to use the component:
import React from 'react';
import {
AppRegistry,
View
} from 'react-native';
import filterData from './filter';
import PopoverFilter from './PopoverFilter';
class MyApp extends React.Component {
render () {
return (
<View>
<PopoverFilter filterData={filterData.filter.track} />
</View>
);
}
}
AppRegistry.registerComponent('MyApp', () => MyApp);