So I am trying to implement a pull to refresh feature in my app. And to say I have successfully implemented this in my Android platform where (in simulator) If I pull down (by mouse) then the refreshing indicator will stay visible until I leave the mouse click and the component will not update until the mouse click as well. It will update the component view when I leave the mouse click and refreshing indicator will be hidden in 2 sec. The similar thing is not exactly working as expected in ios, so when I pull down the screen, the component somehow updates even when I haven't left the mouse click. I have given it a googling but probably haven't been able to find the right search keyword.
Below is code snippet of mine. Thanks in advance.
render() {
const { loadingCart } = this.props;
return (
<View style={styles.container}>
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.contentContainer}
contentInsetAdjustmentBehavior="automatic"
horizontal={false}
refreshControl={this._renderRefreshingControl()}
>
{this._renderProduct()}
{loadingCart && this._renderLoadingCart()}
</ScrollView>
{this._renderCartButton()}
{this._renderAddToCartPopover()}
</View>
);
}
_renderRefreshingControl = () => {
const { refreshing } = this.state;
return (
<RefreshControl refreshing={refreshing} onRefresh={this._handleRefreshingControlVisibility} />
);
};
_handleRefreshingControlVisibility = async () => {
const { fetchProductByCode, navigation } = this.props;
this.setState({
refreshing: true,
});
const resultAction = await fetchProductByCode('38186');
if (resultAction.type === PRODUCT_FETCH_SUCCESS || resultAction.type === PRODUCT_FETCH_FAILURE) {
this.setState({
refreshing: false,
});
};
};
I hope I have been able to clarify my question :-)
Try this out:
_handleRefreshingControlVisibility = () => {
this.setState({ refreshing: true });
setTimeout(function() {
// here do what you want
}, 1500);
};
So the RefreshControl was working as expected, I had a loading flag in my render method, somehow this would be set to false when then refreshing is set true in this.setState({ refreshing: true, })
eventually removing the loading flag solved my issue.
Related
In my home screen I want to auto hide my header in 2 seconds, then I will have a button to show the header when pressed. I have tried with HomeStack.Screen but could not achieve it, I have to create my custom header called HeaderHomeComponent.js and imported it on my homescreen, still I could not achieve it. Please I need help on this issue.
Here is my code:
const [showHeader, setShowHeader] = useState(true);
const onRecord = async () => {
if (isRecording) {
camera.current.stopRecording();
} else {
setTimeout(() => setIsRecording && camera.current.stopRecording(), 23*1000);
const data = await camera.current.recordAsync();
}
};
const visibility = () => {
setTimeout(() => setShowHeader(false), 2000);
}
return (
<View style={styles.container}>
<RNCamera
ref={camera}
type={cameraType}
flashMode={flashMode}
onRecordingStart={() => setIsRecording(true)}
onRecordingEnd={() => setIsRecording(false)}
style={styles.preview}
/>
<HeaderHomeComponent />
You can create a function with useeffect.
Make sure you passs show and handleClose functions from Parent. (Example given below).
const MessageBox = (props) => {
useEffect(() => {
if (props.show) {
setTimeout(() => {
props.handleClose(false);
}, 3000);
}
}, [props.show]);
return (
<div className={`messageBox ${props.show ? "show" : null}`}>
{props.message}
</div>
);
};
UseEffect will be called everytime props.show state will change. And we only want our timer to kick in when the show becomes true, so that we can hide it then.
Also, now to use this, it's simple, in any component.
const [showMessageBox, setShowMessageBox] = useState(false);
return(
<MessageBox
show={showMessageBox}
handleClose={setShowMessageBox} />
);
Also, make sure to handle css, part as well for show and hide.
Simple Example below.
.messageBox {
display: none;
}
.messageBox.show {
display: block;
}
Hope this helps, :-)
You need to do something like this as Mindaugas Nakrosis mentioned in comment
const [showHeader, setShowHeader] = useState(true);
useEffect(() => {
setTimeout(() => setShowHeader(false), 2000);
}, []);
In return where your header is present
{
showHeader && <HeaderHomeComponent/>;
}
I think the approach gonna fit "auto hide and show in 2 seconds", is using Animetad opacity, and giving fix height or/and z-index (as fit you) to the element
// HeaderHomeComponent.js
const animOpacity = useRef(new Animated.Value(1)).current // start with showing elem
//change main view to
<Animated.View
style={{ ...yourStyle... ,
opacity: animOpacity,
}}
>
and then for creating the animation somewhere
() => {
Animated.timing(animOpacity, {
toValue: +(!animOpacity), // the numeric value of not current
duration: 2000, // 2 secs
}).start();
}}
The hieraric location of the declaration of the ref should control usage as calling the effect. maybe you can create useEffect inside the header that can determine if it should be visible or not depends navigation or some other props.
hope its helpful!
React Native App
I am implementing Pull to refresh in my Flatlist prop "onRefresh"
onRefresh called the function "refreshcontrol()" see code below.
I need to change my state "refreshing" to true before I fetch from my API. But it throws the maximum update error.
export default class NotificationScreen extends Component {
constructor(props) {
super(props)
this.state = {
refreshing: false
}
}
.
.
.
.
.
refreshControl() {
const { refreshing } = this.state;
this.setState({ refreshing : true )}. // throws maximum update error
return (
<RefreshControl
refreshing={refreshing}
onRefresh={fetchNotifications.bind(this)} //fetch from API only when refreshing is true
colors={['#2B70AD']}
/>
);
};
}
How else can I set my state to "refreshing : true" ??? Help please!!!!
This is how it was fixed. Solution:
refresh = async() => {
this.setState({refreshing : true})
try {
const notifications = await fetchNotifications();
this.setState({
notifications,
error: null,
refreshing: false
});
} catch (error) {
this.setState({
notifications: [],
error,
refreshing: false
});
}
}
refreshControl() {
const { refreshing } = this.state;
return (
<RefreshControl
refreshing={refreshing}
onRefresh={this.refresh}
colors={['#2B70AD']}
/>
);
};
refreshFlatlist = () => {
this.setState(
{
refresh: true,
},
() => this.getTodosHandler()
);
this.setState({
refresh: false,
});
};
This is how I refresh the default state of course is false. the todosHandler always holds the current todos. Its a call to a SQLite DB stored on the phone locally.
Now the flatlist RefreshComponent I used:
<FlatList
refreshControl={
<RefreshControl
refreshing={this.state.refresh}
onRefresh={this.refreshFlatlist}
/>
}
extraData={this.state.refresh}
data={this.state.toDoArray}
keyExtractor={(item, index) => item.id.toString()}
renderItem={({ item }) => ( ...
Look into it maybe it will help you - this works for me like a charm ;)
This should work perfectly. I think there is a typo in your code. You are using try instead of true. I think that might be the cause of the error.
This might not be the issue but I'll put it here for others (like me) trying to debug:
Make sure that your list is not put inside a ScrollView!
I have a Flatlist which works like a To Do list with a filter for “ToDo” and “Upcoming”. When a user swipes to complete the item, it gets hidden from the list by changing a displayIndex attribute. I would like this to reload the list after the swipe or before the user selects “Upcoming”. After reading through other stack overflow answers I have tried adding extraData={this.state} (and creating a this.state.refresh property which changes after every swipe) to the Flatlist and I also ensured that the list items themselves are React.Components and not PureComponents. I have also tried two ways to hide the ListItems, conditionally rendering them and conditionally changing the style to hidden. Still, I am not seeing any change in my Flatlist.
Below is some partial code to see if there are any gotchas I missed:
In the MainScreen.js
async _addCompletion(myItem) {
//Lots of business logic and after it's done the below code activates
await AsyncStorage.setItem(myItem.key, JSON.stringify(myItem));
await this._updateData();
this.setState({ refresh: !this.state.refresh });
}
render() {
const buttons = ['To Do', 'Upcoming'];
const { displayModeIndex } = this.state;
return (
<View>
<ButtonGroup
onPress={this._updateButtonIndex}
buttons={buttons}
selectedIndex={displayModeIndex}
/>
<FlatList
displayMode={this.state.displayModeIndex}
data={this.state.data}
extraData={this.state}
scrollEnabled={this.state.scrollEnabled}
renderItem={({ item }) => (
<MyListItem
myListItem={item}
addCompletion={this._addCompletion}
displayIndex={this.state.displayModeIndex}
setScrollEnabled={this._setScrollEnabled}
navigation={this.props.navigation}
/>
)}
/>
</View>
);
}
In MyListItem.js
_displayMyItem {
//Logic that determines whether to display a myItem based on several factors. I can confirm this works after refreshing.
}
_hideMyItem = () => {
Animated.timing(this.containerHeight, {
toValue: 0,
}).start(() => {
this.setState({ hidden: true });
});
};
render () {
const {myItem} = this.state;
//Other code that determines how the list item looks depending on myItem data.
return (
//I have also tried to return null if this._displayMyItem(this.state.myItem) returns false
<View style={!this._displayMyItem(this.state.myItem) && { display: 'none' }}>
<Swipeable
onPress={this._onPressRow}
setScrollEnabled={this.props.setScrollEnabled}
addCompletion={this.props.addCompletion}
hideMyItem={this._hideMyItem}
myItem={this.state.myItem}
>
//Other JSX Code
</View>
)
}
The Swipeable is a custom component that calls addCompletion after a swipe and _hideMyItem after everything is done. It is not a PureComponent either.
There's a lot going on here, so I've only included code that seems relevant. I can add more if needed. The addCompletion method is a long
would help some captures...
When you swipe the item , it's just empty right?, if it leaves an empty space try this way of conditional rendering , idk if it would work.
in MyListItem.js
render () {
const {myItem} = this.state;
//Other code that determines how the list item looks depending on myItem data.
return (
//I have also tried to return null if this._displayMyItem(this.state.myItem) returns false
{!this.state.hidden?
<View style={!this._displayMyItem(this.state.myItem) && { display: 'none' }}>
<Swipeable
onPress={this._onPressRow}
setScrollEnabled={this.props.setScrollEnabled}
addCompletion={this.props.addCompletion}
hideMyItem={this._hideMyItem}
myItem={this.state.myItem}
>
//Other JSX Code
</View>:null}
)
}
wich checks if this.state.hidden is false , returns the component, else, returns null
I am developing a light weight project using React Native, and I encountered some setbacks, I couldn't figure it out. :(
I have a page that contains a Yes and a No button and a Yes/No render area, users will be able to click on either of the buttons. According to the users' choice, an avatar will appear in the correct render area (click yes, the avatar will be in the Yes area...). But one user can only be able to click once. I am trying to solve this using state and setState, but couldn't get it to work.
I have:
this.state = {invitedState : false}
and a function (part)
onPress={() => {
if (this.state.invitedState) {
onPress();
}
this.setState(prevState => ({
invitedState: !prevState.invitedState,
}));
}}
Should I not use setState to solve this problem?
thanks!
I think I understand your problem. Something like this?
state = {
toggleUI: true,
userToggled: false
};
handleToggleUI = e => {
this.setState(currentState => {
if ( this.state.userToggled === false ) {
return {
toggleUI: !currentState.toggleUI,
userToggled: true
};
}
});
};
You could try:
onPress{() => {
let tempVar = this.state.invitedState ? false : true;
this.setState({invitedState: tempVar});
}
I'm new to react native.
My screen contains 5 buttons, each one opens the same <Modal>, but the <View> inside it will change depending on the button clicked.
If I click the first button, a text input will be shown into the modal.
If I click the second button, a switch will be shown into the modal.
I've made a modal component (Modal.tsx) :
export default class Modal extends Component {
constructor(props) {
super(props)
}
public render() {
return (
<View style={style.modal} >
{this.props.children}
<View>
)
};
}
// Specific modal implementation with TextInput
const ModalWithTextInput = props => (
<Modal>
<TextInput
value={props.someValue}
/>
<Modal>
)
// Specific modal implementation with Switch
const ModalWithSwitch = props => (
<Modal>
<Switch
value={props.someValue}
/>
<Modal>
)
And now in my 5-button-screen (ButtonsScreen.tsx), I open the right modal depending on the button clicked :
openTextModal = () => {
this.setState({ modalType: 'text' });
}
openSwitchModal = () => {
this.setState({ modalType: 'switch' });
}
These functions are called with, for example, onPress={this.openTextModal}
Finally, I render the modal, to be able to do something like :
<View>
{this.renderModal(modalType)}
</View>
As this :
renderModal = (type) => {
if (type === 'text') {
return <ModalWithTextInput someValue="default text" />
}
if (type === 'switch') {
return <ModalWithSwitch someValue={false}/>
}
}
When I try to open a modal with onPress={this.openTextModal}, nothing happens (no error, no warning).
Anyone can please help ? Thanks.
You need to extract modalType from state, in the render method of your component that displays the Modal.
Clicking the button, only set's state, you need to handle state in the component in order to trigger a refresh. A refresh of the render method will render your Modal changes; React 101.
render() {
const { modalType } = this.state;
return (
<View>
{this.renderModal(modalType)}
</View>
);
}
Based on the fact that this is pretty much my code from your other question. I strongly suggest you take a step back, learn the basic's of React rather than just asking people to piece together solutions which you do not understand. Otherwise the result is you learn very little and have code that you do not understand.