React Native - Edit other component's state - javascript

I want to edit other componenet's state by running function which checks if the current state of the app is active, Which means it runs each time the app come to the forground.
let d = new Date().getHours();
let title = messages[`_${d}`].message;
function getAppState() {
AppState.addEventListener("change", (state) => {
if(state == "active") {
d = new Date().getHours();
title = messages[`_${d}`].message;
// App.setState({ text: title }); **Not working!
console.log(title);
}
});
}
export default class App extends React.Component {
constructor() {
super();
this.state = { text: title };
getAppState();
}
render() {
return (
<View>
<StatusBar hidden={ true } />
<Text style={styles.title}>{title}</Text>
</View>
);
}
}
I want to change text's value according to the hour.

I don't have an environment to test this but I would do it like this:
export default class App extends React.Component {
constructor() {
super();
this.state = {
text: title
};
// Bind updateTile() so `this` corresponds tho the react component and things
// like `this.setState()` are available. Otherwise `this` would be `AppState`
this.updateTitle = this.updateHour.bind(this);
}
componentWillMount() {
// Listen for the event
AppState.addEventListener("change", this.updateTitle);
}
updateTitle(state) {
if (state == "active") {
const d = new Date().getHours();
const title = messages[`_${d}`].message;
this.setState({
text: title
});
console.log(title);
}
}
render() {
return (
<View >
<StatusBar hidden={true} />
<Text style = {styles.title}>{title}</Text>
</View >
);
}
}
If you wanted updateTitle() to be another function and is not part of the Component I would do this:
const updateComponentTitle = state => {
if (state == "active") {
const d = new Date().getHours();
const title = messages[`_${d}`].message;
this.setState({
text: title
});
console.log(title);
}
}
export default class App extends React.Component {
constructor() {
super();
this.state = {
text: title
};
// Bind updateTile() so `this` corresponds tho the react component and things
// like `this.setState()` are available. Otherwise `this` would be `AppState`
this.updateTitle = udpateComponentTitle.bind(this);
}
componentWillMount() {
// Listen for the event
AppState.addEventListener("change", this.updateTitle);
}
render() {
return (
<View >
<StatusBar hidden={true} />
<Text style = {styles.title}>{title}</Text>
</View >
);
}
}

Related

How do i handle HOC from it's wrapped component?

I have a HOC to handling loading using axios,
here's a code of withAxiosHOC:
export default (url, WrapComponent) => {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
isLoading:false,
isFailed:false,
isError:false,
message:null,
};
}
componentDidMount(){
this.callAPI()
}
async callAPI(){
//show loading
// handle API and pass to wrapped component
// hide loading
}
render() {
if (this.state.isLoading) {
return (
//show loading UI
)
} else if(this.state.isFailed){
return(
//show Failed UI
)
} else if(this.state.isError){
return(
//show Error UI
)
}
return (
<WrapComponent data={this.state.data} {...this.props} />
)
}
}
}
and usually i'm used HOC like this, let say Home.js:
export default withAxiosHttp(
'https://reactnative.dev/movies.json',
class Home extends React.Component{
constructor(props) {
super(props);
this.state = {
data:props.data
}
}
render() {
return(
<View style={{flex:1, backgroundColor:Color.black}}>
<MyText>{JSON.stringify(this.state.data, null, 2)}</MyText>
</View>
)
}
}
)
but sometimes i need to call the URL depend on state of my wrapped component,
something like this Suggestion.js:
export default withAxiosHttp(
'https://exampleAPIneedDynamicValue.com/suggestion?lat='+this.state.position.lat+'&long='+this.state.position.long,
class Suggestion extends React.Component{
constructor(props) {
super(props);
this.state = {
data:props.data,
position:{lat:null, long:null}
}
}
componentDidMount(){
let tempPosition = this.state.position
tempPosition.lat = MyLatitude
tempPosition.long = MyLongitude
this.setState({position:tempPosition})
}
render() {
return(
<View style={{flex:1, backgroundColor:Color.black}}>
<MyText>{JSON.stringify(this.state.data, null, 2)}</MyText>
</View>
)
}
}
)
as you see in Suggestion.js, i need to call a URL depending on lat and long of position state,
and lat long state only available in wrappedComponent of HOC,
My Question:
How do i handle HOC to run when lat long state is available in wrappedComponent?
does my HOC can be used to POST method also?
Please give me a suggestion/answer in React Native scope
You can modify the url parameter is a function instead of a string.
In withAxiosHOC
async callAPI(){
axios.get(url(this.state)).then(data => {
//logic here
})
}
Your Suggestion.js will be
export default withAxiosHttp(
(state) => {
return 'https://exampleAPIneedDynamicValue.com/suggestion?lat='+state.position.lat+'&long='+state.position.long
},
class Suggestion extends React.Component{
constructor(props) {
super(props);
this.state = {
data:props.data,
position:{lat:null, long:null}
}
}
componentDidMount(){
let tempPosition = this.state.position
tempPosition.lat = MyLatitude
tempPosition.long = MyLongitude
this.setState({position:tempPosition})
}
render() {
return(
<View style={{flex:1, backgroundColor:Color.black}}>
<MyText>{JSON.stringify(this.state.data, null, 2)}</MyText>
</View>
)
}
}
)

Firebase + React Native - Grab each Document ID

I have been stuck on this for ages trying to figure out how I can console log each Firebase Cloudstore document ID separately when I press onto each rendered FlatList item.
I can grab a certain key / id by using onPress={() =>{console.log(this.state.posts[0].key)}} etc. But I dont know how to grab each one separately. In essence I only want the document ID of the touchableOpacity I have pressed. Not just [0]
Screenshots are below of App layout so you can get an understanding and also code example
PostsLayout.js
export default class PostsLayout extends React.Component {
render() {
const {summary, stringTime, user} = this.props;
return (
<TouchableOpacity
style={styles.container}
onPress={this.props.onPress}
>
<PostsUser user={user}/>
<PostsSummary summary={summary}/>
<PostsDate time={stringTime}/>
</TouchableOpacity>
)
}
}
FlatListLayout.js
export default class FlatListLayout extends React.Component {
render() {
return (
<ScrollView >
<FlatList
data={this.props.data}
renderItem={({item}) => <PostsLayout {...item} onPress={this.props.onPress}/>}
/>
</ScrollView>
)
}
}
ScreenLayout.js
export default class ScreenLayout extends React.Component {
state = {
posts: []
}
db = firebase.firestore()
path = this.db.collection('usersData').doc(firebase.auth().currentUser.uid).collection("posts")
onCollectionUpdate = (querySnapshot) => {
const posts = [];
querySnapshot.forEach((doc) => {
const {summary, time, stringTime, user, userId} = doc.data();
posts.push({
key: doc.id, doc, summary,
time, stringTime, user, userId
});
});
this.setState({
posts
});
}
componentDidMount() {
const {currentUser} = firebase.auth();
this.setState({currentUser})
this.unsubscribe = this.path.onSnapshot(this.onCollectionUpdate)
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return (
<FlatListLayout
data={this.state.posts}
onPress={() => {console.log(this.state.posts[0].key)}}
/>
)
}
}
Thank you for reading this and please help :)
So the easiest fix would be send a function argument from the original press event in the child level.
For example, PostsLayout has the main onPress, so on this call just send back any data you need, each component will have specific data related to the component. As each react child is unique.
PostsLayout.js
export default class PostsLayout extends React.Component {
handleOnPress = () => {
const { onPress, index } = this.props;
if( typeof onPress === 'function') {
onPress(this.props, index); // here pass anything you want in the parent level, like even userm stringtime etc
}
}
render() {
const {summary, stringTime, user} = this.props;
return (
<TouchableOpacity
style={styles.container}
onPress={this.handleOnPress}
>
<PostsUser user={user}/>
<PostsSummary summary={summary}/>
<PostsDate time={stringTime}/>
</TouchableOpacity>
)
}
}
FlatListLayout.js
export default class FlatListLayout extends React.Component {
render() {
return (
<ScrollView >
<FlatList
data={this.props.data}
renderItem={({item, index }) => <PostsLayout {...item} index={index} onPress={this.props.onPress}/>}
/>
</ScrollView>
)
}
}
ScreenLayout.js
export default class ScreenLayout extends React.Component {
state = {
posts: []
}
db = firebase.firestore()
path = this.db.collection('usersData').doc(firebase.auth().currentUser.uid).collection("posts")
onCollectionUpdate = (querySnapshot) => {
const posts = [];
querySnapshot.forEach((doc) => {
const {summary, time, stringTime, user, userId} = doc.data();
posts.push({
key: doc.id, doc, summary,
time, stringTime, user, userId
});
});
this.setState({
posts
});
}
componentDidMount() {
const {currentUser} = firebase.auth();
this.setState({currentUser})
this.unsubscribe = this.path.onSnapshot(this.onCollectionUpdate)
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return (
<FlatListLayout
data={this.state.posts}
onPress={(data, index) => {console.log(data); console.log(this.state.posts[index].key)}}
/>
)
}
}
Let me know if this doesn't make any sense :)

How do I use the props in order to create a condition in ReactJS where I am able to change the state of my app

I have two components, the parent "App" component and the Hero component which contains an image and left and right arrow.
I want to use the right arrow to move the imageIndex + 1 until it reaches the images.length, and I want to have the left arrow to have a condition that I can't subtract if imageIndex = 0.
So something like: ( This part of the code is not added in my code yet because I keep getting undefined)
if (this.props.imageIndex > 0) {
this.setState({
// decrease the imageIndex by 1
})
}
if (this.props.imageIndex < this.props.images.length - 1){
this.setState({
// increase the imageIndex by 1
})
}
will be the condition or something like it.
App.jS (Parent Component)
export default class App extends Component {
constructor() {
super();
this.state = {
language: "english",
render: 'overview',
imageIndex: 0,
}
}
render() {
// to make sure the state changes
console.log(this.state.language)
const {render} = this.state
return <Fragment>
<Hero imageIndex = {this.state.imageIndex} />
</Fragment>;
}
}
How would I add that in my Hero Component which contains this code:
Hero.js
class Hero extends Component {
constructor(props) {
super(props);
this._ToggleNext = this._ToggleNext.bind(this);
}
_ToggleNext(props) {
console.log(this.props.listing.images.length)
console.log(this.props.imageIndex)
}
_TogglePrev(props) {
console.log(this.props.listing.images.length)
console.log(this.props.imageIndex)
}
render() {
const { listing: { images = [], name, location = {} } = {} } = this.props;
return <div className="hero">
<img src={images[0]} alt="listing" />
<a onClick={this._TogglePrev}) className="hero__arrow hero__arrow--left">◀</a>
<a onClick={this._ToggleNext} className="hero__arrow hero__arrow--right">▶</a>
<div className="hero__info">
<p>{location.city}, {location.state}, {location.country}</p>
<h1>{name}</h1>
</div>
</div>;
}
}
const getHero = gql`
query getHero {
listing {
name
images
location {
address,
city,
state,
country
}
}
}
`;
export default function HeroHOC(props) {
return <Query
query={getHero}
>
{({ data }) => (
<Hero
{...props}
listing={data && data.listing || {}} // eslint-disable-line no-mixed-operators
/>
)}
</Query>;
}
One solution is to define the data and functionality in the parent component, in this case App, and pass those down as props to the child which will focus on the rendering.
(code not tested but should give you the basic idea)
class App extends Component {
state = {
imageIndex: 0,
listing: {
images: ['foo.jpg', 'bar.jpg'],
name: 'foo',
location: {...}
}
}
_ToggleNext = () => {
const { imageIndex, listing } = this.state;
if (imageIndex === listing.images.length - 1) {
this.setState({imageIndex: 0});
}
}
_TogglePrev = () => {
const { imageIndex, listing } = this.state;
if (imageIndex === 0) {
this.setState({imageIndex: listing.images.length - 1});
}
}
render() {
return (
<Fragment>
<Hero
listing={this.state.listing}
imageIndex={this.state.imageIndex}
toggleNext={this._ToggleNext}
togglePrev={this._TogglePrev}
/>
</Fragment>
);
}
}
Hero component:
const Hero = props => {
const { listing, imageIndex, togglePrev, toggleNext } = props;
return (
<div className="hero">
<img src={listing.images[imageIndex]}/>
<a onClick={togglePrev})>◀</a>
<a onClick={toggleNext}>▶</a>
<div className="hero__info">
...
</div>
</div>
);
};

React Native - How to append to parent state array while in child component (StackNavigators)?

My project is looping through a data array in a child component Main, and I'm trying to update the state in parent component, App, on an event (swiping right on a card in Main), so that I could access the data that was 'swiped right' on a sibling Component in Favorites. Hopefully that makes sense?
The project structure is as such:
App
|__ Rootstack
|
|__Favorites
|__Main
In my Main component, I am mapping the collection array and looping thru:
collection = imagedata;
// a local JSON array of data that I am looping thru in Main
class Main extends React.Component {
_toFavs = () => {
this.props.navigation.navigate('Favorites');
};
render() {
const contents = collection.map((item, index) => {
return (
<Card key={index}>
......
</Card>
)
});
return (
<View>
<CardStack
onSwiped={() => {console.log('onSwiped')}
onSwipedRight={() => console.log('onSwipedLeft')}>
//
//HERE IS THE PART - HOW TO UPDATE THE 'favoritesList' array in the parent 'App's state?
//
{contents}
</CardStack>
</View>
);
}
}
const RootStack = StackNavigator(
{
Main: {
screen: Main},
Favorites: {
screen: Favorites}
},
{
initialRouteName: 'Main'
}
);
class Favorites extends React.Component {
// The plan is to eventually access the favoritesList array in App's state here and display cards that were swiped right in the Main component.
_onPress = () => {
this.props.navigation.navigate('Main');
};
render() {
return (
<View><Text>Hello!</Text></View>
);
}
}
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
favoritesList: []
};
}
render() {
return <RootStack />;
}
}
I've come across some other answers of updating state such as
this.setState({ favoritesList: [...this.state.favoritesList, 'new value'] }), but how can I do this to the .state of App while i'm inside a child component Main?
Thanks in advance!
collection = imagedata;
// a local JSON array of data that I am looping thru in Main
class Main extends React.Component {
_toFavs = () => {
this.props.navigation.navigate('Favorites');
};
render() {
const contents = collection.map((item, index) => {
return (
<Card key={index}>
......
</Card>
)
});
return (
<View>
<CardStack
onSwiped={() => {console.log('onSwiped')}
onSwipedRight={() => {console.log('onSwipedLeft') ;
this.props.screenProps()}}>
//
{contents}
</CardStack>
</View>
);
}
}
const RootStack = StackNavigator(
{
Main: {
screen: Main},
Favorites: {
screen: Favorites}
},
{
initialRouteName: 'Main'
}
);
class Favorites extends React.Component {
// The plan is to eventually access the favoritesList array in App's state here and display cards that were swiped right in the Main component.
_onPress = () => {
this.props.navigation.navigate('Main');
};
render() {
return (
<View><Text>Hello!</Text></View>
);
}
}
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
favoritesList: []
};
}
updateArr=()=>{consol.log("fire") }
render() {
return <RootStack screenProps={this.updateArr} />;
}
}
i hope it solve your problem
update props-name

React.JS - multiple elements sharing a state ( How do I modify only one of the elements without affecting the others? )

class App extends Component {
constructor(props) {
super(props);
this.state = { Card: Card }
}
HandleEvent = (props) => {
this.SetState({Card: Card.Active}
}
render() {
return (
<Card Card = { this.state.Card } HandleEvent={
this.handleEvent }/>
<Card Card = { this.state.Card } HandleEvent={
this.handleEvent }/>
)
}
}
const Card = props => {
return (
<div style={props.state.Card} onClick={
props.HandleEvent}>Example</div>
)
}
Every time I click on one of the cards all of my elements change states, how do I program this to only change card that I clicked?
Here's a working example
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
0: false,
1: false
};
}
handleEvent(idx) {
const val = !this.state[idx];
this.setState({[idx]: val});
}
render() {
return (
<div>
<Card state={this.state[0]} handleEvent={()=>this.handleEvent(0) } />
<Card state={this.state[1]} handleEvent={()=>this.handleEvent(1) } />
</div>
);
}
}
const Card = (props) => {
return (<div onClick={() => props.handleEvent()}>state: {props.state.toString()}</div>);
}
You can also see it in action here
Obviously this is a contrived example, based on your code, in real world application you wouldn't store hardcoded state like {1: true, 2: false}, but it shows the concept
It's not completely clear from the example what is the Card in the constructor. But here the example of how you can modify clicked element.
Basically you can keep only index of clicked element in parent's state, and then pass it as some property to child component, i.e. isActive here:
const cards = [...arrayOfCards];
class App extends Component {
constructor(props) {
super(props);
this.state = { activeCardIndex: undefined }
}
HandleEvent = (index) => {
this.SetState({
activeCardIndex: index
});
}
render() {
return ({
// cards must be iterable
cards.map((card, index) => {
return (
<Card
key={index}
Card={Card}
isActive={i === this.state.activeCardIndex}
HandleEvent={this.HandleEvent.bind(this, index)}
/>
);
})
});
}
}
const Card = props => {
// style active card
const style = Object.assign({}, props.Card, {
backgroundColor: props.isActive ? 'orange' : 'white',
});
return (
<div style={style} onClick={
props.HandleEvent}>Example</div>
)
}

Categories

Resources