One function does not work inside another in React Native - javascript

I have two functions which I run with a button:
this.state = {
myLimit: this.props.limit.lim,
modalOpen: false,
}
submit = () => {
// this sends state to Redux reducer
let lim = {'lim':this.state.myLimit};
this.props.updateLimit(lim);
// this sends update state for toggle in Parent component
this.props.changeToggle(false);
// function open Modal for 1,5 second like "Success"
showModal = () => {
this.setState({
modalOpen: true
});
this.timeout = setTimeout(() => {
this.setState({
modalOpen: false
})
}, 1500);
}
render(){
return (
<View style={styles.container}>
//some code
<Button onPress={ this.submit } onPressIn={ this.showModal } title='submit' />
<MyPopup visible={this.state.modalOpen}>
<View style={styles.modal}>
<Text style={styles.text}>The limit successfully changed</Text>
</View>
</MyPopup>
</View>
)
}
Parent component
//Parent component
...
this.state = {
openLimit: false,
}
toggle(toggler) {
let togglerStatus = this.state[toggler];
this.setState({
[toggler]: !togglerStatus
});
}
// run changing toggle from child 'Limit'
changeToggle = (val) => {
console.log(val)
this.setState({
openLimit: val
})
};
return(
//some code
<Child changeToggle={this.changeToggle}/>
)
It works, but not always good enough. Sometimes the submit function does not send state to Redux reducer in this this.props.updateLimit(lim) and/or not change toggle state this this.props.changeToggle(false).
So I am tried to combine it in one function:
combineFunc = () => {
// this works
// this sends state to Redux reducer
let lim = {'lim':this.state.myLimit};
this.props.updateLimit(lim)
// this part does not work
// function open Modal for 1,5 second like "Success"
this.setState({
modalOpen: true
});
this.timeout = setTimeout(() => {
this.setState({
modalOpen: false
})
}, 1500);
// this works
// this sends update state for toggle in Parent component
this.props.changeToggle(false);
}
render(){
return (
<View style={styles.container}>
//some code
<Button onPress={this.combineFunc} title='submit' />
</View>
)
}
But in this case it does not change -> this.setState({modalOpen: true}) and Modal does not open at all.
Why does it happen? Is it possible to set some order to run?
I think the problem is in setTimeout, but I need it for a popup.
Any suggestions?

Ciao the problem is in setTimeout as you said. You know this.setState is async. But it has a callback, so try this:
this.setState({
modalOpen: true
}, () => {
this.timeout = setTimeout(() => {
this.setState({
modalOpen: false
})
}, 1500);
});

Related

React Native, how to make redux state false again

I created a view cart in which I show total price and view cart button, when I add item it makes condition true and display that cart below in every screen, but when I click view cart it's not making it false again, how can I do this? can someone check my code and tell me please. Below is my code
Viewcart.js
<View>
{this.props.show && this.props.items.length > 0 ? (
<View style={styles.total}>
<Text style={styles.totaltext}>Total:</Text>
<Text style={styles.priceTotal}>{this.props.total}</Text>
<View style={styles.onPress}>
<Text
style={styles.pressText}
onPress={() => {
RootNavigation.navigate("Cart");
this.props.show;
}}
>
View Cart
</Text>
</View>
</View>
) : null}
</View>
const mapStateToProps = (state) => {
return {
show: state.clothes.show,
};
};
const mapDispatchToProps = (dispatch) => {
return {
showCart: () => dispatch(showCart()),
};
};
reducer.js
if (action.type === SHOW_CART) {
let addedItem = state.addedItems;
if (addedItem.length === 0) {
return {
...state,
show: state.showCart,
};
} else {
return {
...state,
show: action.showCart,
};
}
}
const initialstate = {
showCart: false
}
action.js
export const showCart = (id) => {
return {
type: SHOW_CART,
showCart: true,
id,
};
};
As per the chat the requirement is to toggle this when exiting the screen so the easiest way to do that is to use the lifecycle methods.
To hide use componentDidMount
componentDidMount(){
this.props.showCartOff();
}
to show use component
componentWillUnmount(){
this.props.showCart();
}

How can I display a modal after a delay?

I'm trying to load a modal 2 seconds after the page has been loaded. I tried setting the state on componentDidUpdate but I keep on getting active: undefined The active props determines the visibility of the modal on the page. I tried toggling it to true on browser on the react tool and my modal shows up. I'm just not sure how to load to it 2 seconds after the page loads up.
state = { show: true };
showModal = () => {
this.setState({ show: true });
};
closeModal = () => {
this.setState({ show: false });
};
render() {
const { showModal } = this.state;
return (
<React.Fragment>
....
<Modal.ModalAnimator active={showModal} onClose={this.closeModal}>
<Modal.ModalWithCross
onClose={this.closeModal}
>
<h3>Are you interested in any other Additions?</h3>
<Section>
<p>Hit “notify concierge” and we’ll be in touch shortly.</p>
</Section>
</Modal.ModalWithCross>
</Modal.ModalAnimator>
</React.Fragment>
)
}
When destructuring the state, you write showModal instead of the actual state field name show. So your first lines in the render function should read:
render() {
const { show } = this.state;
return (
<React.Fragment>
...
<Modal.ModalAnimator active={show} onClose={this.closeModal}>
...
Please try this.
state = { show: true };
closeModal = () => {
this.setState({ show: false });
};
componentDidMount() {
setTimeout(() => {
this.setState({ show: true });
}, 2000);
}
render() {
const { showModal } = this.state;
return (
let model = null;
if (this.state.show) {
let model = (
<Modal.ModalAnimator active={showModal} onClose={this.closeModal}>
<Modal.ModalWithCross
onClose={this.closeModal}
>
<h3>Are you interested in any other Additions?</h3>
<Section>
<p>Hit “notify concierge” and we’ll be in touch shortly.</p>
</Section>
</Modal.ModalWithCross>
</Modal.ModalAnimator>
)
}
<React.Fragment>
....
{model}
</React.Fragment>
)
}

(ReactAssign firebase snapshot to a variable outside of the function

Hi i am trying to get the data from my database, in componentDidMount(), it works fine with this :
componentDidMount() {
firebase.database().ref('/users/' + user.uid).once('value').then(function(snapshot) {
var valeur = snapshot.val();
return valeur;
});
this.setState({userData:valeur});
}
I just want to know how can i assign this data to a variable that can be use outside of this function, i tried setstate and using variable that i declare outside nothing work i get :react native possible unhandled promise rejection (id 0) Typeerror: this.setState is not a function. Thank you
class Search extends React.Component {
constructor(props) {
super(props)
this.state = {
films: [],
isLoading: false,
userData: null
}
this.searchedText = ""
this.localData=null;
}
componentWillMount() {
user = firebase.auth().currentUser;
firebase.database().ref('/users/' + user.uid).once('value').then(snapshot => {
pro = snapshot.val()
this.setState({userData: pro});
});
this.setState({ isLoading: true })
getFilmsFromApiWithSearchedText().then(data => this.setState({ films: data.results }))
this.setState({
films: data.results,
isLoading: false
})
console.log(this.state.userData)
}
_displayLoading() {
if (this.state.isLoading) {
return (
<View style={styles.loading_container}>
<ActivityIndicator size='large' />
</View>
)
}
}
_signOut() {
firebase.auth().signOut();
}
_displayDetailForFilm = (idFilm) => {
this.props.navigation.navigate("FilmDetail", { idFilm: idFilm })
}
render() {
return (
<View style={styles.main_container}>
<StatusBar
hidden = {false}
backgroundColor="#F1D145"
></StatusBar>
<TouchableOpacity onPress={() => this._signOut()} style={styles.button} activeOpacity={0.8}>
<Text style={styles.button_text}>Déconnexion</Text>
</TouchableOpacity>
<Text>{user.uid}</Text>
<FlatList
data={this.state.films}
keyExtractor={(item) => item.id.toString()}
renderItem={({item}) => <FilmItem film={item} displayDetailForFilm={this._displayDetailForFilm}/>}
/>
{this._displayLoading()}
</View>
)
}
}
this.setState({ isLoading: true })
getFilmsFromApiWithSearchedText().then(data => this.setState({ films: data.results }))
this.setState({
films: data.results,
isLoading: false
})
console.log(this.state.userData)
}
Here is all the code i already do console.log(this.state.userData) it return null
You have to setState inside the .then function.
Then you can access your value with this.state.userData.
It will be undefined just after mounting then defined when your fetch resolves, so integrate a mechanism that checks for that.
For your undefined problem, a simple solution would be that for instance:
render() {
return (
<View>{this.state.userData && this.state.userData.photo}</View>
)
}
Edit: also like Kai pointed out I am not sure you have access to this instance inside .then callback if you don't use arrow notation.
Switch the anonymous function that you're passing to .then to an arrow function to allow you to use the this from the class scope.
Plenty of information elsewhere on this

Mouseover in map loop reactjs

How to set hover for itself within map in Reactjs
hoverOn = () => {
this.setState({ hover: true});
}
hoverOff = () => {
this.setState({ hover: false });
}
...
var components = posts.map((post, i) => (
.....
<span className={this.state.hover ? 'showtooltip':'hidetooltip'} onHover={this.hoverOn} onMouseOut={this.hoverOff} onClick={(e) => this.viewPost(post, e)}><i className="quickview-button"></i></span>
......
));
Since I use setState, everytime I hover on span it reflects to all list data which returned from map loop.
How can I use hover to reflect on itself element?
Thank so much
wrap this logic in a little Component:
class Foo extends React.Component {
constructor(){
this.state = { hover: false };
}
render(){
return <span
{ ...this.props }
className={ this.state.hover ? 'showtooltip':'hidetooltip' }
onHover={ () => this.setState({ hover: true }) }
onMouseOut={ () => this.setState({ hover: false }) }
/>;
}
}
and use that Component:
...
var components = posts.map((post, i) => (
.....
<Foo onClick={(e) => this.viewPost(post, e)}><i className="quickview-button"></i></Foo>
......
));
You can make hover inside state an object state = { hover: {} } and set it with index i on events i.e. this.setState({ hover: {...this.state.hover, i: true }});.
Then pass i parameter into those methodsonHover={(i) => this.hoverOn(i)}
And change className setup to <span className={this.state.hover[i] ? 'showtooltip':'hidetooltip'} (thanks to #kenny for noticing this)

pass boolean state to multiple of children

The expected
I want to put a loading state in the content of tab, whenever user clicked on the tab title to switch tab, a flag is passed down through children.
The problem
I have this App component, I fake its api call using a setTimeout
class App extends Component {
state = {
loading: false,
data: []
}
getData = () => {
return new Promise(resolve => {
return setTimeout(() => {
resolve(
[
{
id: 1,
name: "Kelas A",
list: ["Jane", "Ali", "Ahmad"]
},
{
id: 2,
name: "Kelas B",
list: ["May", "Henry", "Ben"]
}
]
)
},500)
})
}
async componentDidMount() {
this.setState({
loading: true
})
const data = await this.getData()
this.setState({
data,
loading: false
})
}
//loadingComponent = () => <div>Loading...</div>;
render() {
const { data, loading } = this.state
return (
<Tabs
activeTab={1}
loading={loading}
//loadingComponent={this.loadingComponent()}
>
{data.map(o => (
<Tab
id={o.id}
>
<Tab.Title>{o.name}</Tab.Title>
<Tab.Content>
{o.list.join(", ")}
</Tab.Content>
</Tab>
))}
</Tabs>
);
}
}
I pass loading state as prop to Tabs children component, it worked, I can see true and false:
class Tabs extends Component {
static defaultProps = {
activeTab: 1
};
static getDerivedStateFromProps(nextProps, prevState) {
if(nextProps.loading !== prevState.loading){
return {
loading: nextProps.loading
}
}
}
state = {
activeTab: this.props.activeTab
};
changeTab = tab => {
this.setState({ activeTab: tab });
};
render() {
const { children } = this.props;
const { activeTab, loading } = this.state;
console.log('true or false before pass to children', loading)
return (
<div className="tabs">
{React.Children.map(children, child =>
React.cloneElement(child, {
loading,
activeTab,
changeTab: this.changeTab
})
)}
</div>
);
}
}
But I pass that loading as prop to Tabs's children which is Tab, the loading flag became just false? I can't spot the problem.
class Tab extends Component {
static Title = ({ children, tabId, activeTab, handleTabClick }) => {
return (
<div
className={`title ${tabId === activeTab ? "active" : ""}`}
onClick={handleTabClick}
>
{children}
</div>
);
};
static Content = ({ children, tabId, activeTab, loading }) => {
loading && 'Loading...' //won't work coz loading is always false, I wonder why
return tabId === activeTab ? (
<div className="content">{children}</div>
) : null;
};
render() {
return React.Children.map(this.props.children, child =>
React.cloneElement(child, {
handleTabClick: () => this.props.changeTab(this.props.id),
tabId: this.props.id,
activeTab: this.props.activeTab,
loading: this.props.loading // why always false?
})
);
}
}
My demo
https://codesandbox.io/s/o41r35n2qz
this.props.loading is always false in your child component because it does not even get rendered when it's true, as data is empty when loading is true so data.map does not create any components.
You would need to move the loading check to a parent component that is rendered even when data is empty. Eg. https://codesandbox.io/s/xpy3r3575z
EDIT:
If you want to fetch the data separately for each tab, then you need to create separate API calls, one for fetching tab titles to render the tab headers, and one for fetching the data for the active tab.
Initially when you set it as false
state = {
loading: false,
data: []
}
When the component has been loaded you simulate it to true and then false
async componentDidMount() {
this.setState({
loading: true
})
const data = await this.getData()
this.setState({
data,
loading: false
})
}
But you never received component state changes in child components.
You can get changes of a state using componentWillReceiveProps()
So you can give it a try as
class Tab extends Component {
componentWillReceiveProps(props) { //here you will get changes whenever state changes
let loading = props.loading
this.setState({loading});
}
render() {
return React.Children.map(this.props.children, child =>
React.cloneElement(child, {
handleTabClick: () => this.props.changeTab(this.props.id),
tabId: this.props.id,
activeTab: this.props.activeTab,
loading: this.state.loading //through state
})
);
}
}

Categories