How do I update Different Screens form another independent screen? - react-native

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>
)

Related

react-native Reload Flastlist per each new Focus

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]
}) },
);

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];

Update state when user press back button in React Native

I use react-navigation for manage routes. This is my Home component:
class HomeScreen extends React.Component {
constructor(props) {
this.state = {
userProfile: {
firstname: 'John',
avatar: 'john-profile.png',
location: 'Canada',
}
}
}
componentDidMount() {
AsyncStorage.getItem('userProfile', (errs, result) => {
this.setState({userProfile: JSON.parse(result)});
});
}
render() {
return (
<View>
<Image src="{this.state.userProfile.avatar}" />
<Text>Firstname: {this.state.userProfile.firstname}</Text>
<Text>Location: {this.state.userProfile.location}</Text>
</View>
);
}
}
And this is the Profile screen:
class ProfileScreen extends React.Component {
constructor(props) {
this.state = {
userProfile: null,
}
}
componentDidMount() {
AsyncStorage.getItem('userProfile', (errs, result) => {
this.setState({userProfile: JSON.parse(result)});
});
}
save() {
var userSavedProfile = this.state.userProfile;
userSavedProfile.firstname = "Peter";
userSavedProfile.avatar = "peter-avatar.png";
userSavedProfile.location = "EEUU";
this.setState({userProfile: userSavedProfile});
AsyncStorage.setItem('userProfile', JSON.stringify(this.state.userProfile), () => {});
}
render() {
return (
<View>
<Button title="Save" onPress={() => this.save()} />
</View>
);
}
}
When I save the new user information and I press back button in header (react-navigation) the user profile is old, firstname = John, etc... How update state from Home when user press back button and refresh data?
You can use BackHandler from react-native
https://facebook.github.io/react-native/docs/backhandler.html
You can change state inside function of backhandler
I think that your application would need a state manager, where you could store your user information and access it anywhere in the app. You should take a look at Redux. It would fit your needs and the info in your Home screen would automatically update.
but for anyone who will need this functionality in there react native application here is the solution you can try.
using react navigation.
import {withNavigationFocus} from "react-navigation";
class Profile extends Component {
...
}
export default withNavigationFocus(Profile);
There can be two workarounds check it out -
1 Send callback in params
class HomeScreen extends React.Component {
constructor(props) {
this.state = {
userProfile: {
firstname: 'John',
avatar: 'john-profile.png',
location: 'Canada',
}
}
this.getUserData = this.getUserData.bind(this);
}
componentDidMount() {
this.getUserData;
}
getUserData = () =>{
AsyncStorage.getItem('userProfile', (errs, result) => {
this.setState({userProfile: JSON.parse(result)});
});
}
render() {
return (
<View>
<Image src="{this.state.userProfile.avatar}" />
<Text onPress={()=>this.props.navigation.navigate('ProfileScreen', this.getUserData)}>Firstname: {this.state.userProfile.firstname}</Text>
<Text>Location: {this.state.userProfile.location}</Text>
</View>
);
}
}
class ProfileScreen extends React.Component {
constructor(props) {
this.state = {
userProfile: null,
}
}
componentDidMount() {
AsyncStorage.getItem('userProfile', (errs, result) => {
this.setState({userProfile: JSON.parse(result)});
});
}
save() {
var userSavedProfile = this.state.userProfile;
userSavedProfile.firstname = "Peter";
userSavedProfile.avatar = "peter-avatar.png";
userSavedProfile.location = "EEUU";
this.setState({userProfile: userSavedProfile});
AsyncStorage.setItem('userProfile', JSON.stringify(this.state.userProfile), () => {});
//this is the magic
this.props.navigation.state.params.getUserData();
}
render() {
return (
<View>
<Button title="Save" onPress={() => this.save()} />
</View>
);
}
}
2 On HomeScreen Constructor add this (Dirty one)
this.props.navigation.addListener(
'didFocus',
payload => {
this.setState({is_updated:true});
}
);
You can use componentDidUpdate(){...} insted componentDidMount(){}

Implement FB login with react native and redux

I want to use Redux framework in my react native based app for implementing Facebook login (I am learning Redux at the moment). I am looking for suggestions on how to structure my Facebook login code to use the redux. More specifically, what actions, reducer and store should I create?
Below is the current Facebook based login code that I have in my app (it does not use redux structure). I have deleted the unrelated code to keep things simple:
index.ios.js
class ProjectXApp extends React.Component {
constructor(props) {
// Set the use to NULL
this.state = {
user: null,
};
}
handleLogin(user) {
this.setState({
// Update the user state once the login is complete
user,
});
}
renderScene(route, navigator) {
const Component = route.component;
return (
<View style={styles.app}>
<Component
user={this.state.user}
navigator={navigator}
route={route}
/>
</View>
);
}
render() {
return (
<Navigator
renderScene={this.renderScene.bind(this)}
initialRoute={{
// Render the Login page in the beginning
component: Login,
props: {
onLogin: this.handleLogin.bind(this),
},
}}
/>
);
}
}
Login.js
// Import Facebook Login Util Component
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
// 'false' means responseToken is not required. 'true' means responseToken is required
responseToken: false,
};
}
// This method gets the fb access token, if the token is returned then
// I render the Main App component (switchToMain method). If the
// access token is not returned then I render a login Button (Refer to render method)
async getAccessToken() {
let _this = this;
await (FBSDKAccessToken.getCurrentAccessToken((token) => {
if(!token) {
_this.setState({responseToken: true})
return;
}
_this.setState({responseToken: true});
_this.props.route.props.onLogin({user: true});
_this.switchToMain();
}));
}
switchToMain() {
this.props.navigator.push({
component: Main, // Render the app
props: {
onLogOut: this.onLogOut.bind(this)
}
});
}
componentDidMount() {
this.getAccessToken();
}
onLoginButtonPress() {
// Shows transition between login and Main Screen
this.setState({responseToken: false})
FBSDKLoginManager.logInWithReadPermissions(['public_profile','email','user_friends'], (error, result) => {
if (error) {
alert('Error logging in');
} else {
if (result.isCancelled) {
alert('Login cancelled');
} else {
this.setState({result});
this.getAccessToken();
}
}
});
}
onLogOut() {
this.setState({responseToken: true});
}
render() {
// This component renders when I am calling getAccessToken method
if(!this.state.responseToken) {
return (
<Text></Text>
);
}
// This renders when access token is not available after calling getAccessToken
return (
<View style={styles.container}>
<TouchableHighlight
onPress={this.onLoginButtonPress.bind(this)}
>
<View>
// Login Button
</View>
</TouchableHighlight>
</View>
);
}
}
// Removed the styling code
Logout.js
import { FBSDKLoginManager } from 'react-native-fbsdklogin';
class Logout extends React.Component {
onLogOut() {
FBSDKLoginManager.logOut();
this.props.onLogOut();
this.props.navigator.popToTop();
}
render() {
return (
<View>
<TouchableHighlight
onPress={this.onLogOut.bind(this)}
>
<View
// Styles to create Logout button
</View>
</TouchableHighlight>
</View>
);
}
});
// Removed the styling code
Have you looked at this lib:
https://github.com/lynndylanhurley/redux-auth?