react-native Reload Flastlist per each new Focus - react-native

How to Reload a Screen When web back to screen for second time (in tab navigator):
export default class BasketTab1 extends React.PureComponent {
componentDidMount () {
this.getProductsRequest();//retur
}
getProductsRequest(){
}
render() {
return (
<View style={{margin:5}}>
<FlatList
data={this.state.products}
renderItem={this.renderItem}
keyExtractor={this._keyExtractor}
extraData={this.state}
...)
}
}
I try
set extra data to a boolean value.
extraData={this.state.refresh}
And Toggle the value of boolean state when I want to refresh list
constructor(props) {
super(props);
this.state = {
refresh : false
}
}
componentDidMount () {
this.didFocusListener = this.props.navigation.addListener(
'didFocus',
() => { this.setState({
refresh: !this.state.refresh
}) },
);
this.getProductsRequest();
}
But no reload/not happen anything!
How can I do it?

Have you tried this ?
this.didFocusListener = this.props.navigation.addListener(
'didFocus',
() => { this.setState({
refresh: !this.state.refresh, products: [...this.state.products]
}) },
);

Related

React Native: Getting data from Firebase

I'm simply trying to retrieve data from the database in Firebase, and here's what I've got
var userList = [];
firebase.database()
.ref('/users/')
.once('value')
.then(snapshot => {
snapshot.forEach((doc) => {
userList.push(doc.val());
});
});
console.log(userList);
Even though I copy and pasted this code from a tutorial, the userList is empty outside of the snapshot. Can you tell me why that is?
The request to firebase is asynchronous so console.log(userList); is called before userList.push(doc.val()); gets called.
You should make userList a component state variable so that when you update it your component will re render.
Something like the following should work:
class UserListComponent extends Component {
constructor(props) {
super(props);
this.state = {
userList: [],
};
}
componentDidMount() {
this.getUsers();
}
getUsers() {
firebase
.database()
.ref('/users/')
.once('value')
.then((snapshot) => {
snapshot.forEach((doc) => {
this.setState({
userList: [...this.state.userList, doc.val()],
});
});
});
}
render() {
return (
<View>
{this.state.userList.map((item) => {
return (
<View>
<Text>{item.name}</Text>
</View>
);
})}
</View>
);
}
}

How to refresh data when navigating to the previous page in React Native?

What I'm Trying To Do
When navigating back from second page to first page, I want to refresh datas.
The problem is that when I navigate back from second page, onRefresh() in First doesn't work.
First Page
export default class First extends React.Component {
constructor(props) {
super(props);
this.state = {
refreshing: false,
items: [],
};
}
fetchData = async () => {
const querySnapshot = db.items();
const items = [];
querySnapshot.forEach((doc) => {
items.push(doc.data());
});
this.setState({ items });
}
componentDidMount() {
this.fetchData();
}
componentDidUpdate(prevProps) {
this.onRefresh;
}
onRefresh() {
this.setState({ refreshing: true });
this.fetchData().then(() => {
this.setState({ refreshing: false });
});
}
render() {
return (
<Container>
<Content
refreshControl={(
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
/>
)}
>
</Content>
</Container>
);
}
}
Second Page
this.props.navigation.navigate('First', 'update');
I would appreciate it if you could give me any advices.
Suppose you have two pages PAGE-A & PAGE-B
create a JS-File named ForegroundBackground.js
import React from "react";
import { View } from "react-native";
const ForegroundBackground = ({ navigation, bgCallback, fgCallback }) => {
React.useEffect(() => navigation.addListener('focus', () => {
fgCallback && fgCallback()
}), []);
React.useEffect(() => navigation.addListener('blur', () => {
bgCallback && bgCallback()
}), []);
return (<View/>);
};
export default ForegroundBackground;
you can use it in render function of PAGE-A by providing navigation object to it from screen props.
<ForegroundBackground navigation = {this.props.navigation}
fgCallback = {()=>{alert("resume")}}/>
There are two solutions for the purpose.
To use store.Define a state in store and use that state in pageA and pageB.So if you change store state from pageB.It will auto reflect in entire app.
you can pass a function from pageA to pageB while navigation.The purpose of the function is to refresh state of PageA while moving back. For example:
this.props.navigation.navigate("pageB", {
resetData: () => {
this.setState({
mydata: ""
})
}
})
And while navigating from pageB you can do something like this:
this.props.navigation.state.params.resetData();
this.props.navigation.goBack();
I hope it helps. Leave a comment if you want to have more help/code/discussion etc

How do I update Different Screens form another independent screen?

I have an App with a navigationbar with 2 Screens.
When i apply a function on Screen/Component 1 , I want to render or trigger a change in the Second Screen.
is there a way to either re-render the screen on Enter or to update the state of the other screen ?
Component one:
export default class HomeScreen extends React.Component {
constructor() {
super();
}
_onPress(){
try {
await AsyncStorage.setItem('value', 'changed Value');
} catch (error) {
console.log(error.message);
}
console.log("saved: " + this.state.userName )
}
render() {
return (
<View style={styles.container}>
<Button title="btn" onPress={() => this._onPress()} >
</Button>
</View>
)
}
component 2:
export default class SecondScreen extends React.Component {
constructor() {
super();
this.state = {some : ''}
}
async getValue () {
let recievedValue = '';
try {
let promise = await AsyncStorage.getItem('value') || 'cheeseCake';
promise.then((value) => recievedValue = value)
} catch (error) {
// Error retrieving data
console.log(error.message);
}
return recievedValue
}
render() {
var value= this.getValue();
return (
<View style={styles.container}>
<Text>
HERE CHANGED VALUE: {value}
</Text>
<Button onPress={()=> this.setState((prev)=> {some:'Thing'})}>
</Button>
</View>
)
}
When i press the Button on screen 1(HomeScreen) the value is saved.
But it only shows in the secont screen when I trigger a statechange via Button Press.
How do I render the screen when I visit the screen via navigation bar ?
Did you try EventEmiter?
Use this custom event listener: https://github.com/meinto/react-native-event-listeners
eg:
import { EventRegister } from 'react-native-event-listeners'
/*
* RECEIVER COMPONENT
*/
class Receiver extends PureComponent {
constructor(props) {
super(props)
this.state = {
data: 'no data',
}
}
componentWillMount() {
this.listener = EventRegister.addEventListener('myCustomEvent', (data) => {
this.setState({
data,
})
})
}
componentWillUnmount() {
EventRegister.removeEventListener(this.listener)
}
render() {
return <Text>{this.state.data}</Text>
}
}
/*
* SENDER COMPONENT
*/
const Sender = (props) => (
<TouchableHighlight
onPress={() => {
EventRegister.emit('myCustomEvent', 'it works!!!')
})
><Text>Send Event</Text></TouchableHighlight>
)

React Native - Component update parent

I'm making an app in react native and I'm facing a little problem.
I finished the first layout and now I want to change the style all over the app with a second layout
This is what I have in my parent.
As you can see I use AsyncStorage to check when you open again the app the last selected layout. It all working perfectly.
export default class Home extends React.Component
{
constructor(props){
super(props);
this.state = {
view:0
}
}
componentWillMount()
{
this.checkStructureView();
}
checkStructureView = async() =>
{
const StructureView = await
AsyncStorage.getItem('#StructureView');
if(StructureView == 1)
{
this.setState({
view:1
})
}
else
{
this.setState({
view:0
})
}
}
render()
{
if(this.state.view == 1)
{
return(
<ChangeView/>
...
)
}
else
{
return(
<ChangeView/>
...
)
}
}
}
And this is my component ChangeView. It's a little bit messy because I have for each button active/inactive styles. This is also working perfectly, but the problem is that when I click on the button to change the layout will not change it, only after I refresh the app.
First I added this inside the parent and after I updated the state, the layout has changed instantly but I have more pages where I need to add this component, that's why I'm using an component.
So my question is how can I update instantly the parent state so my layout changes every time I click on the component button without reloading the app.
import React, { Component } from 'react'
import {
View,
Text,
Image,
TouchableOpacity,
AsyncStorage
} from 'react-native'
export default class ChangeView extends Component {
constructor(props){
super(props);
this.state = {
position: this.props.position,
view:0,
view1:require(`../assets/icons/view1_inactive.png`),
view2:require(`../assets/icons/view2_active.png`)
}
}
componentDidMount()
{
this.checkViewStructure();
}
checkViewStructure = async()=>
{
const StructureView = await AsyncStorage.getItem('#StructureView');
if(StructureView == '0')
{
this.setState({
view1:require(`../assets/icons/view1_inactive.png`),
view2:require(`../assets/icons/view2_active.png`)
})
}
else
{
this.setState({
view1:require(`../assets/icons/view1_active.png`),
view2:require(`../assets/icons/view2_inactive.png`)
})
}
}
changeToList = async() =>
{
const StructureView = await AsyncStorage.getItem('#StructureView');
if(StructureView == '0')
{
await AsyncStorage
.setItem('#StructureView', '1')
.then( () => {
//
})
.catch( () => {
alert('Something happened! Please try again later.');
});
this.setState({
view1:require(`../assets/icons/view1_active.png`),
view2:require(`../assets/icons/view2_inactive.png`)
})
}
}
changeToPics = async() =>
{
const StructureView = await AsyncStorage.getItem('#StructureView');
if(StructureView == '1')
{
await AsyncStorage
.setItem('#StructureView', '0')
.then( () => {
//
})
.catch( () => {
alert('Something happened! Please try again later.');
});
this.setState({
view1:require(`../assets/icons/view1_inactive.png`),
view2:require(`../assets/icons/view2_active.png`)
})
}
}
render()
{
if(this.state.position === 0)
return(
<View style={{alignItems:'flex-end',marginTop:20,marginBottom:10,justifyContent:'flex-end',flexDirection:'row'}}>
<View>
<TouchableOpacity
onPress= {() => this.changeToList()}
>
<Image
source={this.state.view1}
style={{width:15,height:21,margin:5}}
/>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity
onPress= {() => this.changeToPics()}
>
<Image
source={this.state.view2}
style={{width:15,height:21,margin:5}}
/>
</TouchableOpacity>
</View>
</View>
)
else
return null
}
}
The ChangeView component only changes state in that specific component. There are several ways of propagating change to the parent component. One way is to implement an onChange prop for the ChangeView component. Your Home component render function would then look like something like this:
render() {
if(this.state.view == 1) {
return(
<ChangeView onChange={ (view) => this.setState({ view }) } />
...
)
} else {
return(
<ChangeView onChange={ (view) => this.setState({ view }) } />
...
)
}
}
You can read more about props here: https://reactjs.org/docs/typechecking-with-proptypes.html
There are other ways of doing this if you have state handler for your application such as Redux.

How to access a row using FlatList keyExtractor in react-native

Is there any way to access a row using key, set using keyExtractor in FlatList.
I using FlatList inorder to populate by data, i need to get a row separately using it's key, inorder to update that row without re-render the entire view.
on componentWillMount i populated datalist array using an api call.
dummy array look this
[{id:"Usr01",title:"Name1"},{id:"Usr02",title:"Name2"},...]
while press on any row i get it's id, i need to access that row using it's key.
let dataitem = this.state.datalist[id];
while i console dataitem i get undefined
i set id as the key in keyExtractor, is there any way to do the same.
My code look like this
FlatListScreen.js
export default class FlatListScreen extends Component {
constructor(props)
{
super(props);
this.state={
datalist: [],
}
}
componentWillMount() {
ApiHandler.getlistitem('All').then(response =>{
this.setState({datalist: response});
});
}
_keyExtractor = (item, index) => item.id;
_onPressItem = (id) => {
let dataitem = this.state.datalist[id];
const { name } = dataitem
const newPost = {
...dataitem,
name: name+"01"
}
this.setState({
datalist: {
...this.state.datalist,
[id]: newPost
}
})
};
_renderItem ({ item }) {
return (
<MyListItem
id={item.id}
onPressItem={this._onPressItem}
title={item.title}
/>
)
}
render() {
return (
<FlatList
data={this.state.datalist}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
}
}
MyListItem.js
export default class MyListItem extends Component {
constructor(props) {
super(props)
this.state = {
title: '',
id: ''
}
}
componentWillMount() {
const { title, id } = this.props
this.setState({ title, id })
}
componentWillReceiveProps(nextProps) {
const { title, id } = nextProps
this.setState({ title, id })
}
shouldComponentUpdate(nextProps, nextState) {
const { title} = nextState
const { title: oldTitle } = this.state
return title !== oldTitle
}
render() {
return (
<View>
<TouchableOpacity onPress={() =>this.props.onPressItem({id:this.state.id})}>
<View>
<Text>
{this.props.title}
</Text>
</View>
</TouchableOpacity>
</View>
);
}
}
I think changing
onPressItem({id:this.state.id});
to
onPressItem(this.state.id); in your child component
OR
_onPressItem = (id) => { }
to
_onPressItem = ({id}) => { }
in your parent component will solve the issue.
As you are sending it as an object from child to parent and you can access it like this also
let dataitem = this.state.datalist[id.id];