It seems that i miss something during my first react native project:
The render function is not rendering the view when i try to display an array which is assigned to state.
i can only console log the array (in the setState callback) while also render the view when i comment out the part where i refer to the array.
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
array: [],
token: '',
}
}
setLoading = () => {
this.setState({ loading: true }, () => this.login())
}
login = async () => {
try {
const response = await
fetch(`http://10.0.2.2`);
const res = await response.json();
this.setState({
name: res.name,
address: res.address,
token: res.token,
}, function () {
this.setState({
array: res.array,
loading: false
}, function () {
//only reaching when commented out the array-part in the render
//function
console.log(this.state.array[0].grade)
});
})
}
catch{
console.log('in CATCH ERROR')
} }
render() {
if (this.state.loading) {
return (
<View>
<ActivityIndicator />
</View>
);
}
return this.state.token ? (
<View >
<Text> {this.state.name}</Text>
//this works !
<Text> {this.state.array[0].prof}</Text>
//only rendering without the mapping of the array
{/*this.state.array.map((item, i) => (
<div key={i}>
<Text >Grade : {item.grade}</Text>
<Text >Class : {item.class}</Text>
<Text >Proffessor : {item.prof}</Text>
</div>
))*/}
</View>
) : (
<View>
<TextInput
value={this.state.username}
onChangeText={(username) => this.setState({ username })}
placeholder={'Username'}
/>
<TextInput
value={this.state.password}
onChangeText={(password) => this.setState({ password })}
placeholder={'Password'}
secureTextEntry={true}
/>
<Button
title={'Login'}
style={styles.input}
onPress={this.login.bind(this)}
/>
</View>
)
};
according to the docs this should be possible.. any Pro here that has an idea why this is happening?
Related
I have two components one is Messages and other component is Navigation it's a stack navigator component. I would like to call function named onRefresh of Messages component from component Navigation header buttons. Please see my code how can I achieve this.
Messages.js (component file)
export default class Messages extends Component {
// Constructor
constructor(props) {
super(props)
this.state = {
messages: [],
isLoading: true
};
}
// End Constructor
// Getting Messages from server
async getMessages() {
try {
const response = await fetch('https://reactnative.dev/movies.json');
const json = await response.json();
this.setState({ messages: json.movies });
} catch (error) {
console.log(error);
} finally {
this.setState({ isLoading: false });
}
}
// End Getting messages from server
componentDidMount() {
this.getMessages();
}
// On refresh the messages
onRefresh = async () => {
this.setState({isLoading: true,}, () => {this.getMessages();});
}
// Send now sms using button.
sendNowMessage = async (title) => {
Alert.alert(title, "asdasdas");
}
render() {
const { messages, isLoading } = this.state;
return (
<SafeAreaView style={styles.container}>
{isLoading ? <ActivityIndicator size="large" color="#0000ff" style={ styles.horizontal } /> : (
<FlatList
data={ messages }
keyExtractor={({ id }, index) => id}
onRefresh={() => this.onRefresh()}
refreshing={this.state.isLoading}
renderItem={({ item }) => (
<View style={styles.item}>
<Text style={{ marginBottom: 12, }}>{item.title}, {item.releaseYear}</Text>
<Button
onPress={() => this.sendNowMessage(item.title)}
title="Send SMS"
style={styles.sendSMS}
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>
</View>
)}
/>
)}
</SafeAreaView>
);
}
}
Navigation.js (component file)
const Stack = createStackNavigator();
export default function Navigation() {
return (
<Stack.Navigator>
<Stack.Screen
name="AuthScreen"
component={AuthScreen}
options={{
title: "Welcome",
}}
/>
<Stack.Screen
name="Messages"
component={Messages}
options={{
headerTitle: 'Messages',
headerRight: () => {
return (
<View style={styles.headerButtons}>
<View style={{ marginRight:10, }}>
<TouchableOpacity
onPress={() => {
new Messages().onRefresh() <---- I am calling it like this.
}}
>
<Text
style={styles.actionButtons}
>
<Ionicons name="reload-sharp" size={20} color="black" />
</Text>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity
onPress={() => {
alert("working")
}}
>
<Text
style={styles.actionButtons}
>
<Ionicons name="filter" size={20} color="black" />
</Text>
</TouchableOpacity>
</View>
</View>
)
}
}}
/>
</Stack.Navigator>
);
}
I am calling onRefresh function like this new Messages().onRefresh() and getting the following error.
Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the Messages component.
I want to learn what is the best way to call onRefresh function.
When I run my react-native component, I get the following error:
However, in the debugger in constructor and componentWillmount and render lifecycle, isEdit is false.
Only componentDidmount is not printing anything, so I think the component is not running componentDidmount lifecycle and I don't know the error reason for this.
constructor(props: IProps) {
// isEdit: false
super(props);
// const {navigation} = this.props;
props.navigation.setOptions({
headerRight: () => {
return <HeaderRightBtn onSubmit={this.onSubmit} />;
},
});
}
componentWillmount() {
// isEdit: false
}
componentDidmount() {
// isEdit of undefined
}
render() {
// isEdit: false
const {categorys, isEdit} = this.props;
const {myCategorys} = this.state;
const classifyGroup = _.groupBy(categorys, (item) => item.classify);
return (
<ScrollView style={styles.container}>
<Text style={styles.classifyName}>我的分类</Text>
<View style={styles.classifyView}>
<DragSortableView
dataSource={myCategorys}
renderItem={this.renderItem}
sortable={isEdit}
fixedItems={fixedItems}
keyExtractor={(item) => item.id}
onDataChange={this.onDataChange}
parentWidth={parentWidth}
childrenWidth={itemWidth}
childrenHeight={itemHeight}
marginChildrenTop={margin}
onClickItem={this.onClick}
/>
{/* {myCategorys.map(this.renderItem)} */}
</View>
<View>
{Object.keys(classifyGroup).map((classify) => {
return (
<View key={classify}>
<Text style={styles.classifyName}>{classify}</Text>
<View style={styles.classifyView}>
{classifyGroup[classify].map((item, index) => {
if (
myCategorys.find(
(selectedItem) => selectedItem.id === item.id,
)
) {
return null;
}
return this.renderUnSelectedItem(item, index);
})}
</View>
</View>
);
})}
</View>
</ScrollView>
);
}
}
Correct names of methods are
“componentDidMount()” and “UNSAFE_componentWillMount()” as per componentdidmount and unsafe_componentwillmount respectively.
Note that componentWillMount() is changed to UNSAFE_componentWillMount() as per the official docs.
I want to make a generic handler for setstate rather than having two different handlers? How can I do that?
I am thinking of something like:
setStateHandler= (stateToBeChanged) =>{
this.setState({
stateToBeChanged: !this.state.stateToBeChanged
});
}
handleHappyToggle = () =>{
this.setState({
Happy: !this.state.Happy
});
}
handleSadToggle = () => {
this.setState({
Sad: !this.state.Sad
});
}
My two touchable opacities:
<TouchableOpacity
onPress={this.handleHappyToggle}
>
<Text> {this.state.happy ? 'Yes' : 'No' } </Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.handleSadToggle}
>
<Text> {this.state.sad? 'Yes' : 'No' } </Text>
</TouchableOpacity>
EDIT: added demo StackSnippet...
EDIT 2: added 3 total ways to handle this "generically"
You could use a generic handler to accomplish this - just supply the "mood" as a parameter.
handleMoodToggle = mood => event => {
if(!["Happy", "Sad"].includes(mood)){
return null;
}
this.setState({
[mood]: !this.state[mood]
})
}
<TouchableOpacity onPress={this.handleMoodToggle("Happy")}>
<Text> {this.state.happy ? 'Yes' : 'No'} </Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.handleMoodToggle("Sad")}>
<Text> {this.state.sad ? 'Yes' : 'No'} </Text>
</TouchableOpacity>
DEMO:
const { Component } = React;
const { render } = ReactDOM;
class App extends Component {
state = {
Happy: true,
Sad: false
};
handleMoodToggle1 = mood => event => {
if (!["Happy", "Sad"].includes(mood)) {
return null;
}
this.setState({
[mood]: !this.state[mood]
})
};
handleMoodToggle2 = event => {
this.setState({
[event.target.innerHTML]: !this.state[event.target.innerHTML]
});
}
handleMoodToggle3 = event => {
this.setState({
[event.target.name]: !this.state[event.target.name]
});
}
render() {
return (
<div>
<button onClick={this.handleMoodToggle1("Happy")}>Toggle Happy</button>
<button onClick={this.handleMoodToggle1("Sad")}>Toggle Sad</button>
<br />
<button onClick={this.handleMoodToggle2}>Happy</button>
<button onClick={this.handleMoodToggle2}>Sad</button>
<br />
<button name="Happy" onClick={this.handleMoodToggle3}>Happy 3</button>
<button name="Sad" onClick={this.handleMoodToggle3}>Sad 3</button>
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</div>
);
}
}
render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
you can check my exmaple for you:
export default class App extends Component {
state = {
emotions: [
{ name: 'happy', status: true },
{ name: 'sad', status: false },
{ name: 'angry', status: true },
]
}
handleEmotionToggle = (index) => {
let emotions = [...this.state.emotions]
emotions[index].status = !emotions[index].status
this.setState({
emotions
})
}
render() {
return (
<View style={styles.container}>
{
this.state.emotions.map((emotion, index) => {
return (
<TouchableOpacity key={index}
onPress={()=>this.handleEmotionToggle(index)}
>
<Text style={{margin:10,fontSize:20}}>{emotion.name} - {emotion.status ? "YES":"NO"} </Text>
</TouchableOpacity>
)
})
}
</View>
);
}
}
I'm new at react-native. I try a lot of ways to get arrays of JSON data from openweathermap's API but didn't work. I want some suggestions and guidelines.
I just fetch JSON data and want to set state more than one object from JSON data.
Example of JSON data. I want to get temp, humidity, description and icon from 2 arrays in list[].
"list":[
{
"dt":1541440800,
"main":{
"temp":25.85,
"temp_min":25.85,
"temp_max":27.35,
"pressure":1022.17,
"sea_level":1023.04,
"grnd_level":1022.17,
"humidity":100,
"temp_kf":-1.5
},
"weather":[
{
"id":500,
"main":"Rain",
"description":"light rain",
"icon":"10n"
}
],
"dt_txt":"2018-11-05 18:00:00"
},
{
"dt":1541451600,
"main":{
"temp":26.38,
"temp_min":26.38,
"temp_max":27.5,
"pressure":1021.34,
"sea_level":1022.24,
"grnd_level":1021.34,
"humidity":100,
"temp_kf":-1.12
},
"weather":[
{
"id":802,
"main":"Clouds",
"description":"scattered clouds",
"icon":"03n"
}
],
"dt_txt":"2018-11-05 21:00:00"
}
Here is my JSON fetching component. I want to show different data from arrays by passing state to front-end component.
export default class Forecast extends Component {
constructor(props) {
super(props);
this.state = {
hourlyForecast: {}
};
}
fetchData = () => {
fetch(
`http://api.openweathermap.org/data/2.5/forecast?q=${
this.props.currentCity
}&units=metric&appid=${API_KEY}`
)
.then(response => response.json())
.then(json => {
this.setState({
hourlyForecast: {
temp: json.list[0].main.temp,
humidity: json.list[0].main.humidity,
icon: json.list[0].weather[0].icon,
description: json.list[0].weather[0].description,
date_time: json.list[0].dt_txt
}
});
})
.catch(error => {
console.warn(error);
});
};
componentDidMount = () => this.fetchData();
render() {
return (
<Container>
<Content>
<ForecastCards {...this.state.hourlyForecast} />
<ForecastCards {...this.state.hourlyForecast} />
</Content>
</Container>
);
}
}
And here is my front-end component.
export default class ForecastCards extends Component {
render() {
return (
<Card>
<CardItem>
<Body>
<View style={{flex: 1, flexDirection: "row"}}>
<View>
<Text>{this.props.date_time}</Text>
<Text>Temperature: {this.props.temp} °C</Text>
<Text>Humidity: {this.props.humidity} %</Text>
</View>
<View style={{marginLeft: 100}}>
<Image
style={{ width: 60, height: 60 }}
source={{
uri: `http://openweathermap.org/img/w/${
this.props.icon
}.png`
}}
/>
<Text>{this.props.description}</Text>
</View>
</View>
</Body>
</CardItem>
</Card>
);
}
}
Here you go with a solution
fetchData = () => {
fetch(
`http://api.openweathermap.org/data/2.5/forecast?q=${this.props.currentCity}&units=metric&appid=${API_KEY}`
)
.then(response => response.json())
.then(json => {
this.setState({
hourlyForecast: {
temp: json.list[0].main.temp,
humidity: json.list[0].main.humidity,
icon: json.list[0].weather[0].icon,
description: json.list[0].weather[0].description,
date_time: json.list[0].dt_txt
}
});
})
.catch(error => {
console.warn(error);
});
};
componentDidMount = () => this.fetchData();
render() {
return (
<Container>
<Content>
<ForecastCards hourlyForecast={this.state.hourlyForecast} />
<ForecastCards hourlyForecast={this.state.hourlyForecast} />
</Content>
</Container>
);
}
And here is my front-end component.
export default class ForecastCards extends Component {
render() {
return (
<Card>
<CardItem>
<Body>
<View style={{flex: 1, flexDirection: "row"}}>
<View>
<Text>{this.props.hourlyForecast.date_time}</Text>
<Text>Temperature: {this.props.hourlyForecast.temp} °C</Text>
<Text>Humidity: {this.props.hourlyForecast.humidity} %</Text>
</View>
<View style={{marginLeft: 100}}>
<Image
style={{ width: 60, height: 60 }}
source={{
uri: `http://openweathermap.org/img/w/${
this.props.hourlyForecast.icon
}.png`
}}
/>
<Text>{this.props.hourlyForecast.description}</Text>
</View>
</View>
</Body>
</CardItem>
</Card>
);
}
}
ForecastCards.propTypes = {
hourlyForecast: PropTypes.object
}
Setting the state is fine. For passing the props <ForecastCards hourlyForecast={this.state.hourlyForecast} />
Provide the PropType in ForecastCards component using ForecastCards.propTypes = {
hourlyForecast: PropTypes.object
}
For access date_time or any other key, please use this.props.hourlyForecast.date_time or this.props.hourlyForecast.{key}
Hope this will help you.
I can't scroll my page. What is wrong?
export default class CustomersTab extends Component {
constructor(props) {
super(props);
this.state = {
token: "",
isLoading: true,
dataSource: null
};
}
componentWillMount() { MY APİ CODES - HERE İS NOT İMPORTANT
)
.then(response => response.json())
.then(responseData => {
this.setState({
isLoading: false,
dataSource: responseData.result
});
});
})
.catch(error => console.warn(error));
}
static navigationOptions = {
title: "Firmaların Listesi",
tabBarIcon: <Icon name="ios-people" style={{}} />
};
render() {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator />
</View>
);
} else {
return this.state.dataSource.map((val, key) => {
return (
<List key={key}>
<ListItem
roundAvatar
avatar={{ uri: "url/notimportant" }}
key={key}
title={val.name+" "+val.surname}
subtitle={val.customerType}
/>
</List>
);
});
}
}
}
I tried different codes but it did not improve. How can I view all the data from the page? The "Load others" button may also work. Does anyone have an idea??
Note: DATA is ok. The problem is scroll.
Your render elements should be wrapped withing ScrollView.
return (
<ScrollView>
{this.state.dataSource.map((val, key) => {
return (
<List key={key}>
<ListItem
roundAvatar
avatar={{ uri: "url/notimportant" }}
key={key}
title={val.name + " " + val.surname}
subtitle={val.customerType}
/>
</List>
);
})}
</ScrollView>
);
you can use FlatList and ScrollView. remember to import them. if you see the react-native docs and read it you can use it. another tag that you use it like View don't scroll.