I'm trying to create a React Native app for managing categories and items. It uses React Native Store to save all local data. The app should not and will not use an internet API. It will be local and offline only.
I managed to push items to the DB when calling the addCategory(title) function but it does not update the state inside categories.js
Since there is a spectacular lack of documentation concerning the state in react-native, I wondered if anyone here knows how to make it update through the listview and when changing from the categories to the items to details Components.
I'm using React Native 0.26
index.ios.js:
... // default imports
import Store from 'react-native-store'
const categorieslist = require('./categories')
const DB = {
'categories': Store.model('category'),
'items': Store.model('item')
}
class testapp extends Component {
componentDidMount() {
this.reload()
}
reload() {
DB.categories.find().then(resp => this.setState({
categories: resp
}))
}
addCategory(title) {
var newTitle = title;
DB.categories.add({
title: newTitle,
games: []
})
this.reload()
}
render() {
return (
<View>
<NavigatorIOS
...
initialRoute={{
component: categorieslist,
title: 'Categories',
rightButtonTitle: 'New',
onRightButtonPress: () => {this.addCategory('title')},
passProps: {
categories: this.state.categories
}
}}
/>
</View>
)
}
}
... // AppRegistry registerComponent and stuff
and categories.js:
... // default imports
class categories extends Component {
constructor(props) {
super(props)
var ds = new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2})
this.state = {
dataSource: ds.cloneWithRows(props.categories)
}
}
render() {
if (this.state.dataSource.getRowCount() === 0) {
... // shows 'no categories' screen
} else {
return(
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <View><Text>{rowData}</Text></View>}
/>
)
}
}
}
module.exports = categories
State in react-native is the same as react (web). So you can refer to the documentation here: https://facebook.github.io/react/docs/component-api.html
state is local to each component. In your reload method you setState to this which is the testapp Component and not the categories Component.
You must pass your state to the categories component via props.
Related
Is there a way I can fix this without using redux?
This is only happening on iOS, on android the AddListener works perfectly fine without it.
I have a component and I call the props.navigation.addListener on the componentDidMount functon.
Some code to help understand exactly where it breaks:
componentDidMount(){
var _this = this;
this.willBlurListener = this.props.navigation.addListener('willBlur', () => {
_this.timer.clearTimeout();
});
this.willFocusListener = this.props.navigation.addListener('willFocus', () => {
_this._action();
});
AppState.addEventListener('change', this._handleAppStateChange);
}
And then I use the component like this:
<Inactivity name='SomeNameView' navigation={ this.props.navigation }>
{this.renderDetails()}
</Inactivity>
Can you please try to use withNavigation function, it returns a HOC that has navigation in it props so you don't have to pass from the parent component to the child:
I created a simple app that uses this concept that probably can help you:
import React from 'react';
import {
View,
Text,
Button,
} from 'react-native';
import {
createStackNavigator,
withNavigation,
} from 'react-navigation';
class SomeComponent extends React.Component {
componentDidMount() {
this.willBlurListener = this.props.navigation.addListener('willBlur', () => {
this.someAction();
})
}
someAction() {
console.log('Some action is called!');
}
componentWillUnmount() {
this.willBlurListener.remove();
}
render() {
return (
<View>
<Text>Some Component</Text>
<Button
title={'Open settings'}
onPress={() => this.props.navigation.navigate('Settings')}
/>
</View>
)
}
}
const SomeComponentWithNavigation = withNavigation(SomeComponent);
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Home'
}
render() {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<SomeComponentWithNavigation/>
<Text>Welcome to home screen!</Text>
</View>
)
}
}
class SettingsScreen extends React.Component {
static navigationOptions = {
title: 'Settings'
}
render() {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Welcome to settings screen!</Text>
</View>
)
}
}
export default createStackNavigator(
{
Home: HomeScreen,
Settings: SettingsScreen,
},
);
I have used import { useNavigation } from '#react-navigation/native'; to achieve this. This could work for you as well.
Sample code example
import { useNavigation } from '#react-navigation/native';
class CurrentOrderClass extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.onFocusSubscribe = this.props.navigation.addListener('focus', () => {
// Your code
});
}
componentWillUnmount() {
this.onFocusSubscribe();
}
.
.
.
.
function CurrentOrder(props) {
const navigation = useNavigation(props)
return <CurrentOrderClass {...props} navigation={navigation} />
}
}
export default CurrentOrder;
You can also check to React Native docs https://reactnavigation.org/docs/navigation-events/
I found this a bit tricky and after looking into it for a bit, I come up with the following solution. Note that is tested on React Navigation 5.x.
import { useIsDrawerOpen } from "#react-navigation/drawer";
let lastDrawerStateIsOpened = false;
const DrawerComponent = (props) => {
const isOpened = useIsDrawerOpen();
if (lastDrawerStateIsOpened != isOpened) {
lastDrawerStateIsOpened = isOpened;
if (isOpened) {
// Do what needs to be done when drawer is opened.
}
}
};
Also, note that I'm using a functional component.
I am new to React Native and trying to create a simple iOS app. The app has a button on clicking which I need to store the timestamp of the click on the device in a file.
I know that React Native has an API called AsyncStorage but I am getting errors while using this. I copied the code from some site on the net.
Can someone please guide me to use this API?
This is my entire code:
import React, {Component} from 'react';
import { StyleSheet, Text, View, TextInput, AsyncStorage } from 'react-native';
export default class App extends Component{
state = {
'name': ''
}
componentDidMount = () => AsyncStorage.getItem('name').then((value) => this.setState({'name': value}))
setName = (value) => {
AsyncStorage.setItem('name': value);
this.setState({'name': value});
}
render() {
return (
<View style={styles.container}>
<TextInput style = {styles.textInput} autoCapitalize = 'none'
onChangeText = {this.setName}/>
<Text>
{this.state.name}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
marginTop: 50
},
textInput: {
margin: 15,
height: 35,
borderWidth: 1,
backgroundColor: '#7685ed'
}
});
As for the error, when I launch the code on iOS, I am getting a red screen. There is no syntax error that I can see.
Thanks in advance.
Hard to say without more detail the exact problem you're facing, but I assume some of the following might help you?
Ah I see you posted some code. You will need a constructor that defines your state as well. Added it in my code below.
Please note I'm not an expert. Forgive any errors
import {
AsyncStorage,
} from 'react-native';
class myComponent extends React.Component{
constructor(props) {
super(props);
this.state = {
data: null
};
}
componentDidMount() {
this._loadInitialState().done();
}
_someFunction() {
var myData = 123;
saveItemLocally('data', myData);
}
async _loadInitialState() {
try {
// get localy stored data
var dataStored = await AsyncStorage.getItem('data');
if (dataStored!==null) {
this.setState({
data: dataStored
});
}
} catch (error) {
//didn't get locally stored data
console.log(error.message);
}
} // end _loadinitialstate
render () {
//your render function
return (
);
}
} // end of your component
async function saveItemLocally(item, value) {
try {
await AsyncStorage.setItem(item, value);
} catch (error) {
console.log('AsyncStorage error: ' + error.message);
}
}
hi my dear friends i have problem in my simple code in react native
when i run the code i` giving this error. apologize i'm nub in react native :)
import React, { Component } from 'react';
import {
ListView,
View
} from 'react-native';
import MyPresentationalComponent1 from './MyPresentationalComponent1'
export default class MyContainerComponent extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !==
r2});
this.state = {
dataSource: ds.cloneWithRows([
'Item1', 'Item2', 'Item3', 'Item4', 'Item5', 'Item6', 'Item7',
'Item8',
'Item9', 'Item10'
])
};
}
render() {
return (
<View>
<MyPresentationalComponent1 dataSource = {this.state.dataSource}/>
</View>
);
}
}`
I am confused with the rerender mechanism of listview.
Page 1 have render a listview with two item, then I click 'Add' button, navigate to another page, and add one item to Page 1's datasource, then navigator back.
What I expect to see is Page 1 with three item, but actually is Page 2, listview is disappeared. But if I use mouse/finger touch it, listView come out again with three item.
I have test it on my iphone and simulator
Page 1's source code:
class Market extends Component {
constructor(props) {
super(props)
this.state = {
dataSource: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }),
}
}
componentDidMount() {
this.refreshListView(this.props.data)
}
componentWillReceiveProps(nextProps) {
this.refreshListView(nextProps.data)
}
refreshListView() {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(data)
})
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
refreshControl={
<RefreshControl/>
}
/>
)
}
const mapStateToProps = createSelector(
selectData(),
(data) => ({
data,
})
)
export default connect(mapStateToProps)(Market)
Just found something that has fixed mine! try adding removeClippedSubviews={false} to your ListView
https://github.com/facebook/react-native/issues/8607#issuecomment-231371923
I simply want to print a couple of arrays on screen in my React Native iOS app. Right now nothing is showing up on screen. console.logs after the for loop show that the arrays have the data they are supposed to have, but it is not reflected on screen. How do I render again after the fetch call has loaded? Here's my code:
'use unique'
import React, { Component } from 'react';
import {
StyleSheet,
TouchableHighlight,
Text,
View,
ListView
} from 'react-native';
import api from './apicall';
class APIRequest extends Component {
constructor() {
super();
this.state = {
users: [],
id: [],
type: []
}
}
componentWillMount() {
api.getData()
.then((res) => {
this.setState({
users: res
})
for (var i = 0; i < this.state.users.length; i++) {
this.state.id.push(this.state.users[i].id);
this.state.type.push(this.state.users[i].type);
});
}
render() {
return(
<View style={styles.container}>
<Text style={styles.buttonText}>
{this.state.id}
</Text>
</View>
);
}
}
this.state.id.push(this.state.users[i].id);
this.state.type.push(this.state.users[i].type);
That's the wrong way to set new state. You should do the following:
var idArray = [];
var typeArray = [];
for (var i = 0; i < this.state.users.length; i++) {
idArray.push(this.state.users[i].id);
typeArray.push(this.state.users[i].type);
}
this.setState({id: idArray});