Reputation: 530
I am fairly new to React Native and currently try to implement pull-to-refresh functionality in my app. Here is my Parent
component snippet:
function MainScreenBoss({ navigation }) {
const [refreshing, setRefreshing] = useState(false);
//here I'm trying to refresh (example from docs)
const onRefresh = React.useCallback(async () => {
setRefreshing(true);
wait(2000).then(setRefreshing(false));
}, [refreshing]);
return (
<ScrollView
contentContainerStyle={styles.containerScroll}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}>
<View style={styles.container}>
//other components here
<View style={styles.containerWidget}>
//need to refresh this component
<Widget style={styles.widget} />
</View>
</View>
</ScrollView>
);
}
I am trying to refresh Widget component as it has the call to API url in it with loading animation. Here is the snipped of Widget
component:
function Widget() {
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
//call to api returns data
useEffect(() => {
getData(API_GET_WIDGET_DATA, setLoading, setData);
}, []);
return (
<View style={styles.container}>
{isLoading ? (
<ActivityIndicator
loader="blue"
visible={isLoading}
style={styles.animation}
/>
) : (
<>
<View style={styles.Contents}>
//need to refresh data is it used in here when loading is finished
</View>
</>
)}
</View>
);
}
I guess I need to force update the widget component or launch the loading function again somehow, but I do not quite understand what should I do.
Edit: the api function looks like this:
export function getData(reqOptions, setLoading, setData) {
fetch(apiURL, reqOptions)
.then((response) => response.json())
.then((json) => setData(json.data))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}
Upvotes: 1
Views: 4698
Reputation: 1458
If I understand well, in order to update your widget, you gotta re-do the fetch that you have inside your useEffect
.
The useEffect
you currently have only executes on mount of the component, as the dep. array is empty. From the looks of your parent component, the Widget does not get unmounted, therefore your useEffect
is only called once.
What you have to do is to take your refreshing
state and pass it to the Widget, so that it knows when it has to refetch the data.
Try something like this:
function MainScreenBoss({ navigation }) {
const [refreshing, setRefreshing] = useState(false);
//here I'm trying to refresh (example from docs)
const onRefresh = React.useCallback(async () => {
setRefreshing(true);
wait(2000).then(setRefreshing(false));
}, [refreshing]);
return (
<ScrollView
contentContainerStyle={styles.containerScroll}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}>
<View style={styles.container}>
//other components here
<View style={styles.containerWidget}>
//need to refresh this component
<Widget style={styles.widget} refreshing={refreshing} /> // pass this prop
</View>
</View>
</ScrollView>
);
}
Widget:
function Widget(props) {
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
//call to api returns data
useEffect(() => {
getData(API_GET_WIDGET_DATA, setLoading, setData);
}, []);
useEffect(() => {
if (props.refreshing) {
getData(API_GET_WIDGET_DATA, setLoading, setData);
}
}, [props.refreshing]); // on every refreshing `true` state
return (
<View style={styles.container}>
{isLoading ? (
<ActivityIndicator
loader="blue"
visible={isLoading}
style={styles.animation}
/>
) : (
<>
<View style={styles.Contents}>
//need to refresh data is it used in here when loading is finished
</View>
</>
)}
</View>
);
}
Edit:
You can either modify the existing useEffect
to include the check of the data
, or else create another one which runs only on mount, with another useEffect
running on update state. I've added the second case.
Edit 2:
There is nothing wrong with that, actually that is better because the refreshing is handled at a correct time, when the call is finished and the data is fulfilled. With your current config the call may not be finished, but after 2 seconds you're setting refreshing to false, this may bring problems.
The beauty of hooks is that you can use as many of them as you want, as compared to the class methods of React before hooks introduction. So there is no problem with that, but you can actually change it around and have something like:
useEffect(() => {
if (!data || props.refreshing) {
getData(API_GET_WIDGET_DATA, setLoading, setData);
}
}, [data, props.refreshing]);
If you may, one last thing: I would not pass state setters to your fetch fn, I would handle the state updates in the component and just make sure that the fn returns your data. It's just for separation of concerns causes.
Upvotes: 1