react-native-picker-select not Working with Parent View {flexDirection:'row'} - reactjs

I want to use <RNPickerSelect./> with a <TextInput/> in a single row. So, when I make the Parent flexDirection: row, I see only the arrow and no text. Even if I remove the <TextInput/>, I don't see any text in the Picker.
import React, { Component } from 'react';
import {
StyleSheet,
View,
} from 'react-native';
import RNPickerSelect from 'react-native-picker-select';
type Props = {}
const countryCode = [
{
label: '+91',
value: '+91',
},
{
label: '+1',
value: '+1',
},
{
label: '+2',
value: '+2',
},
];
export default class PickerTest extends Component<Props> {
constructor() {
super()
this.state = {
phoneNumber: "",
countryCode: ""
}
}
render() {
return (
<View style={{flexDirection:'row'}}>
<View paddingVertical={5}>
{/* and hiding the InputAccessoryView on iOS */}
<RNPickerSelect
placeholder={{}}
items={countryCode}
onValueChange={value => {
this.setState({
countryCode: value,
});
}}
InputAccessoryView={() => null}
style={pickerSelectStyles}
value={this.state.countryCode}
/>
</View>
</View>
);
}
}
const pickerSelectStyles = StyleSheet.create({
inputIOS: {
fontSize: 16,
paddingVertical: 12,
paddingHorizontal: 10,
borderWidth: 1,
borderColor: 'gray',
borderRadius: 4,
color: 'black',
paddingRight: 30, // to ensure the text is never behind the icon
},
inputAndroid: {
fontSize: 16,
paddingHorizontal: 10,
paddingVertical: 8,
borderWidth: 0.5,
borderColor: 'purple',
borderRadius: 8,
color: 'black',
paddingRight: 30, // to ensure the text is never behind the icon
},
});
On running the above app, I get something like this
As you can see the picker is not showing the text.
Below are the configuration I am using
react-native-picker-select version: 6.3.3
react-native version: 0.61.2
react version: 16.9.0

this is an upstream issue: https://snack.expo.io/HkygCqhsr
options:
useNativeAndroidPickerStyle prop
set width and height with inputAndroid style prop

Add the atribute "pickerProps" to the RNPickerSelect with the option overflow: 'hidden'
<RNPickerSelect style={styles.selectContainer}
...
pickerProps={{ style: { height: 214, overflow: 'hidden' } }}
...
/>

Related

Generate random QR in react-native

I have generated QR Code manually by asking user to input value but what I'm trying is to generate QR Code random(Qr should be generated from number) without asking input from user so how can I generate random QR Code by pressing button only? I'm working on project that requires random QR Code every time when they open their mobile app.
Here is my code:
import React, { Component } from "react";
import {
StyleSheet,
View,
TextInput,
TouchableOpacity,
Text,
} from "react-native";
import QRCode from "react-native-qrcode-svg";
class QrGenerator extends Component {
constructor() {
super();
this.state = {
// Default Value of the TextInput
inputValue: "",
// Default value for the QR Code
valueForQRCode: "",
};
}
getTextInputValue = () => {
this.setState({
valueForQRCode: this.state.inputValue,
});
};
render() {
return (
<View style={styles.MainContainer}>
<QRCode
//Setting the value of QRCode
value={"ComputerGen" + this.state.valueForQRCode}
//Size of QRCode
size={250}
//Background Color of QRCode
bgColor="#000"
//Front Color of QRCode
fgColor="#fff"
getRef={(ref) => (this.svg = ref)}
onPress={() => {}}
/>
<TextInput // Input to get the value to set on QRCode
style={styles.TextInputStyle}
onChangeText={(text) => this.setState({ inputValue: text })}
underlineColorAndroid="transparent"
placeholder="Enter text to Generate QR Code"
/>
<TouchableOpacity
onPress={this.getTextInputValue}
activeOpacity={0.7}
style={styles.button}
>
<Text style={styles.TextStyle}> Generate QR Code </Text>
</TouchableOpacity>
</View>
);
}
}
export default QrGenerator;
const styles = StyleSheet.create({
MainContainer: {
flex: 1,
margin: 10,
alignItems: "center",
paddingTop: 40,
},
TextInputStyle: {
width: "100%",
height: 40,
marginTop: 20,
borderWidth: 1,
textAlign: "center",
},
button: {
width: "100%",
paddingTop: 8,
marginTop: 10,
paddingBottom: 8,
backgroundColor: "#F44336",
marginBottom: 20,
},
TextStyle: {
color: "#fff",
textAlign: "center",
fontSize: 18,
},
});
What you want to do is to make a random string "inputValue" if it is empty.
getTextInputValue = () => {
if (this.state.inputValue.length === 0) {
const randomInputValue = Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
this.setState({
valueForQRCode: randomInputValue,
inputValue: randomInputValue,
});
} else {
this.setState({
valueForQRCode: this.state.inputValue,
});
}
};

Can't save values for a react-native TextInput which is created dynamically

I am trying to create dynamic TextInputs, and then saving the values of TextInput into another state by indexing through loops, but I am not getting the desired output.
I think the problem is in the loop because the console.log of the onChangeText for the value of i is always the number of TextInputs.
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View,TextInput, FlatList, Button, } from 'react-native';
export default function App() {
const [value,setValue]=useState(1)
const[textInput,setTextInput]=useState([])
const[inputData,setInputData]=useState([{index:0,text:''},{index:1,text:''},{index:2,text:''}])
useEffect(()=>{
addTextInput()
},[value])
const addTextInput=()=>{
var Input=[];
for(var i=0;i<value;i++){
Input.push(
<TextInput style={styles.textInput}
onChangeText={(text)=>{
var newarray = [...inputData]
var index = newarray.findIndex((dd) =>dd.index==i)
console.log('index',index,'i',i); //------>idk why but the value of i is always the no of text
// input pls hwlp me with
// these loop.i think i have done something wrong in here
newarray[index] = {index:i,text:text}
setInputData(newarray)} }
/>)
}
setTextInput(Input);
}
return (
<View style={styles.container}>
<View style={{height:50,color:'green',backgroundColor:'green',marginBottom:20}}/>
<View style={{flexDirection:'row',justifyContent:'space-around'}}>
<Text style={{fontSize:20,alignSelf:'center'}}>No Of Tenents:</Text>
</View>
<View style={{flexDirection:'row',justifyContent:'space-around'}}>
<Button title='1' onPress={()=>setValue(1)}/>
<Button title='2' onPress={()=>setValue(2)}/>
<Button title='3' onPress={()=>setValue(3)} />
</View>
<FlatList data={textInput}
renderItem={({item})=>{
return item
}}/>
</View>
);
}
const pickerSelectStyles = StyleSheet.create({
inputIOS: {
fontSize: 16,
paddingVertical: 12,
paddingHorizontal: 10,
borderWidth: 1,
borderColor: 'gray',
borderRadius: 4,
color: 'black',
paddingRight: 30,
},
inputAndroid: {
fontSize: 16,
paddingHorizontal: 10,
paddingVertical: 8,
borderWidth: 1,
borderColor: 'green',
borderRadius: 8,
color: 'black',
paddingRight: 50,
},
});
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
textInput: {
height: 40,
borderColor: 'black',
borderWidth: 1,
margin: 20
},
});
Use let in loop to declared i, because let can be only available inside the scope it's declared.[read this]
If you want remove loop,
const addTextInput = () => {
var Input = [...textInput];
if (Input.length < value) {
const InputIndex = Input.length;
Input.push(
<TextInput
style={styles.textInput}
onChangeText={text => {
var newarray = [...inputData];
const index = newarray.findIndex(dd => dd.index === InputIndex);
newarray[index] = {index: index, text: text};
setInputData(newarray);
console.log(newarray);
}}
/>,
);
} else {
Input.pop();
}
setTextInput(Input);
};
Using let instead of var solved my isuue.

How do I access a component's inner method using hooks in react-native

I am using the this, and am trying to use the "close" method. How might I call that close method once I hit a cancel alert as shown below? I believe this is my general misunderstanding of how react hooks/classes work with public methods. Any help would be appreciated!
<Swipeable
renderRightActions={renderRightActions}
onSwipeableRightOpen={() => {
Alert.alert(
"Delete " + title + "?",
"",
[
{
text: "Cancel",
onPress: () => {this.close()},
style: "cancel",
},
{ text: "Delete", onPress: () => removeItem(id) },
],
{ cancelable: true }
);
}}
leftThreshold={0}
rightThreshold={150}>Random stuff in here</Swipeable>
EDIT I rewrote it as a class:
import React from "react";
import { StyleSheet, Alert, Text, View, TouchableWithoutFeedback, Animated } from "react-native";
import { Ionicons } from "#expo/vector-icons";
import Swipeable from "react-native-gesture-handler/Swipeable";
import * as Haptics from "expo-haptics";
class ListItem extends React.Component {
constructor(props) {
super(props);
const { id, title, onClick, drag, isActive, removeItem } = this.props;
this.id = id;
this.title = title;
this.onClick = onClick;
this.drag = drag;
this.isActive = isActive;
this.removeItem = removeItem;
}
componentDidUpdate = () => {
if (this.isActive) {
// haptic feedback
Haptics.impactAsync();
}
};
renderRightActions = (progress, dragX) => {
const trans = dragX.interpolate({
inputRange: [0, 50, 100, 101],
outputRange: [-20, 0, 0, 1],
});
return (
<View style={{ width: 45, marginTop: 20, marginLeft: 1000 }}>
<Animated.Text
style={{
transform: [{ translateX: trans }],
}}></Animated.Text>
</View>
);
};
onCancel() {
console.log("WHAT");
console.log(this);
}
render() {
return (
<Swipeable
renderRightActions={this.renderRightActions}
onSwipeableRightOpen={function () {
Alert.alert(
"Delete " + this.title + "?",
"",
[
{
text: "Cancel",
onPress: this.onCancel(),
style: "cancel",
},
{ text: "Delete", onPress: () => removeItem(id) },
],
{ cancelable: true }
);
}}
leftThreshold={0}
rightThreshold={150}>
<TouchableWithoutFeedback
onPress={function () {
this.onClick();
}}
onLongPress={this.drag}
delayLongPress={200}>
<View style={listItemStyles.item}>
<View
style={{
marginTop: 7,
marginLeft: 18,
}}>
<Text style={listItemStyles.itemTitle}>{this.title}</Text>
</View>
<View style={listItemStyles.itemIcon}>
<Ionicons
name={"ios-arrow-forward"}
size={30}
style={{ marginBottom: -3 }}
color={"#E3E3E3"}
/>
</View>
</View>
</TouchableWithoutFeedback>
</Swipeable>
);
}
}
export default ListItem;
// Styles
const listItemStyles = StyleSheet.create({
// list item
item: {
padding: 3,
marginVertical: 2,
marginLeft: "2%",
marginRight: "2%",
width: "96%",
// internals
flexDirection: "row",
justifyContent: "space-between",
},
itemTitle: {
flexDirection: "row",
justifyContent: "flex-start",
color: "#333333",
fontSize: 18,
},
itemSubtitle: {
fontSize: 12,
marginLeft: 5,
},
itemIcon: {
marginTop: 2,
marginRight: 10,
flex: 1,
flexDirection: "row",
justifyContent: "flex-end",
},
});
However when I print out (this), "close" does not show up as a method.
You cannot use this because it references your outer component due to your fat-arrow functions.
The docs say Using reference to Swipeable it's possible to trigger some actions on it. close: method that closes component.
The problem here is that the great thing about the fat arrow function (it does not has its own refernce, so the outer is used).
Here in your case, that means your outer component function that render the Swipeable component.
So for that use case, you need to use a class component for the onSwipeableRightOpen prop.

accessing this.props.navigation.state.params ouside render

I am passing a parameter from ListView to a detailed page. Can I access this parameter outside the render on my detailed page:
Below is my code to pass the parameter:
<ListView
dataSource={this.state.dataSource}
renderRow = {( rowData ) =>
<TouchableOpacity style = { styles.item } activeOpacity = { 0.4 } onPress = { this.clickedItemText.bind( this, rowData ) }>
<Text style = { styles.text }>{ rowData.ser }</Text>
</TouchableOpacity>
}
renderSeparator = {() =>
<View style = { styles.separator }/>
}
enableEmptySections = { true }
/>
</View>
clickedItemText( clickedItem )
{
this.props.navigation.navigate('Item', { item: clickedItem });
}
I can get the parameter value on my detailed page using the code below:
<View style = { styles.container2 }>
<Text style = { styles.text }>You Selected: { this.props.navigation.state.params.item.Location.toUpperCase() }</Text>
</View>
I need to do this.props.navigation.state.params.item.Location ouside render. The reason I want to do that because I want to create a ListView on my detailed page by filtering my Json data based on the passed parameter so for e.g. if the parameter passed is 2 then I want to filter my JSON data based on 2 and create another ListView.
As far as I know filtering of the JSON data can only be done outside render. I could be wrong though, I am new to react Native.
Below is my entire class for detailed page.
import React, { Component } from 'react';
import { StyleSheet, Text, View, ListView, ActivityIndicator, TextInput } from 'react-native';
import ServiceDetails from '../reducers/ServiceDetails';
class ServiceListDetails extends Component
{
constructor() {
super();
var newList = ServiceDetails.filter(obj => obj.fk === this.props.navigation.state.params.item.id);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(ServiceDetails),
};
}
static navigationOptions =
{
title: 'SecondActivity',
};
ListViewItemSeparator = () => {
return (
<View
style={{
height: .5,
width: "100%",
backgroundColor: "#000",
}}
/>
);
}
render()
{
return(
<View style={styles.MainContainer}>
<ListView
dataSource={this.state.dataSource}
renderSeparator= {this.ListViewItemSeparator}
renderRow={(rowData) => <Text>{rowData.addr}</Text>}
/>
</View>
);
}
}
const styles = StyleSheet.create(
{
MainContainer:
{
justifyContent: 'center',
flex:1,
margin: 10
},
TextStyle:
{
fontSize: 23,
textAlign: 'center',
color: '#000',
},
rowViewContainer:
{
fontSize: 17,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10,
},
ActivityIndicator_Style:
{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
left: 0,
right: 0,
top: 0,
bottom: 0,
},
TextInputStyleClass:{
textAlign: 'center',
height: 40,
borderWidth: 1,
borderColor: '#009688',
borderRadius: 7 ,
backgroundColor : "#FFFFFF"
}
});
export default ServiceListDetails;
Below is screen shot of the error that I am getting on detailed page.
Below is the entire code of my Master Page that has a list View on it and it works fine:
import React, { Component } from 'react';
import { Text, View, StyleSheet, ListView, ActivityIndicator, TextInput, TouchableOpacity } from 'react-native';
import { Provider, connect } from 'react-redux';
import { createStore } from 'redux'
import reducers from '../reducers/ServiceReducer';
import ServiceItem from './ServiceItem';
import Icon from 'react-native-vector-icons/EvilIcons';
import ServiceDetail from './ServiceDetail';
import TestActivity from './TestActivity';
import { StackNavigator } from 'react-navigation';
import ServiceListDetails from './ServiceListDetails' ;
class AutoCompActivity extends Component {
constructor(props) {
super(props);
this.state = {
// Default Value of this State.
Loading_Activity_Indicator: true,
text:'',
}
this.arrayholder=[];
}
componentDidMount() {
const data = require('../reducers/services.json')
let ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.setState({
Loading_Activity_Indicator: false,
dataSource: ds.cloneWithRows(data),
}, function() {
// In this block you can do something with new state.
this.arrayholder = data ;
});
}
SearchFilterFunction(text){
const newData = this.arrayholder.filter(function(item){
const itemData = item.ser.toUpperCase()
const textData = text.toUpperCase()
return itemData.indexOf(textData) > -1
})
this.setState({
dataSource: this.state.dataSource.cloneWithRows(newData),
text: text
})
}
ListViewItemSeparator = () => {
return (
<View
style={{
height: .5,
width: "100%",
backgroundColor: "#000",
}}
/>
);
}
/*Navigate_To_Second_Activity=(ser)=>
{
//Sending the JSON ListView Selected Item Value On Next Activity.
this.props.navigation.navigate('Second', { JSON_ListView_Clicked_Item: ser });
}*/
clickedItemText( clickedItem )
{
this.props.navigation.navigate('Item', { item: clickedItem });
}
static navigationOptions =
{
title: 'MainActivity',
};
render()
{
if (this.state.Loading_Activity_Indicator) {
return (
<View style={styles.ActivityIndicator_Style}>
<ActivityIndicator size = "large" color="#009688"/>
</View>
);
}
return (
<View style={styles.MainContainer}>
<TextInput
style={styles.TextInputStyleClass}
onChangeText={(text) => this.SearchFilterFunction(text)}
value={this.state.text}
underlineColorAndroid='transparent'
placeholder="Search Here"
/>
<ListView
dataSource={this.state.dataSource}
renderRow = {( rowData ) =>
<TouchableOpacity style = { styles.item } activeOpacity = { 0.4 } onPress = { this.clickedItemText.bind( this, rowData ) }>
<Text style = { styles.text }>{ rowData.ser }</Text>
</TouchableOpacity>
}
renderSeparator = {() =>
<View style = { styles.separator }/>
}
enableEmptySections = { true }
/>
</View>
);
}
}
export default MyNewProject= StackNavigator(
{
First: {screen: AutoCompActivity},
Item: {screen: ServiceListDetails}
}
);
const styles = StyleSheet.create(
{
MainContainer:
{
justifyContent: 'center',
flex:1,
margin: 10
},
TextStyle:
{
fontSize: 23,
textAlign: 'center',
color: '#000',
},
rowViewContainer:
{
fontSize: 17,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10,
},
ActivityIndicator_Style:
{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
left: 0,
right: 0,
top: 0,
bottom: 0,
},
TextInputStyleClass:{
textAlign: 'center',
height: 40,
borderWidth: 1,
borderColor: '#009688',
borderRadius: 7 ,
backgroundColor : "#FFFFFF"
},
separator:
{
height: 2,
backgroundColor: 'rgba(0,0,0,0.5)'
},
item:
{
padding: 15
},
text:
{
fontSize: 18
},
});
My services.json is below
[
{
"id":0,
"ser": "Test Service",
"Location": "TestLoc",
"Phone1":"(999)-999-5050",
"SecondLoc": "TestLoc2",
"email":"test#test.com",
"sourceLat":"33.977806",
"sourceLong":"-117.373261",
"destLatL1":"33.613355",
"destLongL1":"-114.596569",
"destLatL2":"33.761693",
"destLongL2":"-116.971169",
"destAddr1": "Test Address, Test Drive",
"destAddr2": "Test Address2, Test Drive2",
"onlineURL":"",
"Phone2": "(900)-900-3333"
}
]
My ServiceDetails.json is below:
[
{
"id":"1",
"fk": "0",
"addr": "2Test addr",
"phone": "(951)-955-6200",
"LatL":"33.935547",
"Long2":"-117.191",
"Online": ""
},
{
"id":"2",
"fk": "0",
"addr": "testaddr21",
"phone": "(999)-999-9999",
"LatL":"33.977880",
"Long2":"-117.1234",
"Online": ""
}
]
How can I achieve this?

undefined is not a function (evaluating 'fetch('apiurl')')

I am using below versions
react-native-router-flux ^3.39.1
react-native 0.44.0
I expecting that will call API which I am using with "fetch"
Have used componentDidMount but it's showing another error
undefined is not an object (evaluating 'this._component.getScrollableNode')
But I am getting below error outputs
Steps to reproduce
Create three scene using router flux (In my case App, Login, Home)
Use ScrollView for creating the Login.js Create a button
using TouchableHighlight after that call the fetch with a function
using onPress like onPress={ () => this.fetchData() }
Below code, I am using for App.js
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
AsyncStorage,
} from 'react-native';
import Login from './components/Login'
import Register from './components/Register'
import Home from './components/Home'
import { Scene, Router, TabBar, Modal, Schema, Actions, Reducer, ActionConst } from 'react-native-router-flux'
const reducerCreate = params=>{
const defaultReducer = Reducer(params);
return (state, action)=>{
console.log("ACTION:", action);
return defaultReducer(state, action);
}
};
export default class App extends Component {
constructor(props, context) {
super(props, context);
this.state = {
logged: false,
loading: true,
};
};
componentWillMount(){
self = this;
AsyncStorage.getItem('token')
.then( (value) =>{
if (value != null){
this.setState({
logged: true,
loading: false,
});
}
else {
this.setState({
loading: false,
})
}
});
};
render() {
if (this.state.loading) {
return <View><Text>Loading</Text></View>;
}
return (
<Router>
<Scene hideNavBar={true} key="root">
<Scene key="logIn" component={Login} title="Login" initial={!this.state.logged}/>
<Scene key="regisTer" component={Register} title="Register"/>
<Scene key="home" component={Home} title="home" initial={this.state.logged}/>
</Scene>
</Router>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
And below code, using for Login.js
/* #flow */
import React, { Component } from 'react';
import {
View,
StyleSheet,
Image,
ScrollView,
TextInput,
Text,
TouchableHighlight,
Alert,
} from 'react-native';
import { Container, Content, InputGroup, Input, Icon, Item } from 'native-base';
import Button from 'react-native-button'
import {Actions} from 'react-native-router-flux'
import ResponsiveImage from 'react-native-responsive-image'
export default class Login extends Component {
constructor(props){
super(props)
this.state = {
email: '',
password: '',
data: '',
}
}
fetchData() {
fetch('http://allstariq.tbltechnerds.com/api/login/?username=andress&password=23434')
.then((response) => response.json())
.then((responseData) => {
this.setState({
data: responseData.movies,
});
})
.done();
}
render() {
return (
<View style={styles.container}>
<ScrollView>
<View style={ styles.logoContainer }>
<View style={{flexDirection: 'row',}}>
<ResponsiveImage
source={require('../assets/logo.png')}
initWidth="300"
initHeight="160" />
</View>
</View>
<View style={ styles.formContainer }>
<Item>
<Icon active name='mail' />
<Input
onChangeText={(text) => this.setState({email: text})}
value={this.state.email}
placeholder='Email'/>
</Item>
<Item>
<Icon active name='key' />
<Input
onChangeText={(text) => this.setState({password: text})}
value={this.state.password}
placeholder='Password'/>
</Item>
<TouchableHighlight
style={ styles.loginButton }
onPress={ () => this.fetchData() }>
<Text style={ styles.btnText}>Login</Text>
</TouchableHighlight>
</View>
<View style={ styles.bottomContainer }>
<Text style={ styles.cenText }>Dont worry if you haven't an account yet . . </Text>
<Text
style={ styles.blueText}
onPress={ Actions.regisTer }
>Register Now</Text>
</View>
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
logoContainer: {
flex: .5,
padding: 10,
justifyContent: 'center',
alignItems: 'center',
},
logoItem: {
width: null,
height: null,
resizeMode: 'cover',
},
formContainer: {
flex: 4,
padding: 10,
},
inputelm: {
marginBottom: 10,
backgroundColor: '#999',
borderWidth: 0,
fontSize: 20,
color: '#FFF',
fontFamily: 'AmaticSC-Bold',
},
loginButton: {
borderRadius: 3,
marginBottom: 20,
marginTop: 20,
paddingLeft: 10,
paddingRight: 10,
backgroundColor: '#2196f3',
elevation: 4,
},
signupButton: {
borderRadius: 3,
marginBottom: 20,
marginTop: 20,
paddingLeft: 10,
paddingRight: 10,
backgroundColor: '#7cb342',
elevation: 4,
},
btnText: {
textAlign: 'center',
color: '#FFF',
fontSize: 30,
lineHeight: 40,
},
blueText: {
textAlign: 'center',
color: '#2196f3',
fontSize: 20,
lineHeight: 40,
},
bottomContainer: {
flex: 1,
padding: 10,
},
cenText: {
textAlign: 'center',
fontSize: 16,
},
});
What is the actual way to use fetch with react-native-router-flux? I am new to react, please help me.
well, its a couple of months late but the problem here is that your fetchData method hasn't got access to this because when you declare a method like this:
fetchData() {
}
Under the hood react is creating a function this way:
this.fetchData = function() {
}
when you declare a function using the function keyword, everything between {} will have its own "this" and your method will not have access to the "this" of the component context.
That is the reason why you are getting the "undefined", because you are calling "this.setState" inside the promise returned by fetch, so the error is not fetch, but this.
To solve this issue you could just define your method this way:
fetchData = () => {
}
And because the functions defined with a fat arrow do not create their own scope, the component "this" will be available from inside your method.
Did you try maybe to import the library?
Import fetch from "fetch";
You have not imported the fetch function, import it from the node-fetch module like
import fetch from 'node-fetch'

Resources