Why am i getting error: Too many rerenders in react native - react-native

I am trying to pass an array to another component. Data is a json object array with data in it. I know the data is there because when i click the button, the handleclick method is called and the data is displayed but when i try to pass that data as an array (seasondisplay and seasonlength) I get an error: Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. How do i pass the data from the fetch as an array to the dropdown box component?
function HomeScreen({ navigation }) {
const [list, setList] = useState([]);
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
let seasons = [];
useEffect(() => {
fetch('http://localhost:3000/person', {
credentials: "same-origin"
})
.then((response) => response.json())
.then((json) => setData(json))
.catch((error) => console.log("junk" + error))
.finally(() => setLoading(false));
}, []);
const [display, setDisplay] = useState("");
const handleClick = () => {
setDisplay(
data.map((item, i) => (
<Text key={i}> {item.lastname} </Text>
)))
}
const [seasondisplay, setSeasonDisplay] = useState("");
const [seasonlength, setSeasonLengthDisplay] = useState(0);
setSeasonDisplay(
data["lastname"]
)
setSeasonLengthDisplay (
2
)
return (
<View style={styles.container}>
<View style={styles.innerContainer}>
<DropDownChoice whichDropDown="Season" HowMany={seasonlength} ItemChoices={seasondisplay} />
<DropDownChoice whichDropDown="Veggies" HowMany={4} ItemChoices={["carrots", "turnip", "peas", "corn"]} />
<DropDownChoice whichDropDown="Fruit" HowMany={3} ItemChoices={["apples", "oranges", "bananas"]} />
<Button
title="Connect to DB"
onPress={() => {
console.log("please work");
console.log(data);
handleClick();
}}
here is json:
[{"personid":11,"lastname":"cook","firstname":"ben","address":"north","city":"london"},{"personid":22,"lastname":"smith","firstname":"elaine","address":"main","city":"milton"}]

I suspect that:
You are changing state on each render by setSeasonDisplay. This state change cause re render and again in rendering you changing state. You have a loop.
function HomeScreen({ navigation }) {
const [list, setList] = useState([]);
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
let seasons = [];
useEffect(() => {
fetch('http://localhost:3000/person', {
credentials: "same-origin"
})
.then((response) => response.json())
.then((json) => setData(json))
.catch((error) => console.log("junk" + error))
.finally(() => setLoading(false));
}, []);
const [display, setDisplay] = useState("");
const handleClick = () => {
setDisplay(
data.map((item, i) => (
<Text key={i}> {item.lastname} </Text>
)))
}
const [seasondisplay, setSeasonDisplay] = useState("");
const [seasonlength, setSeasonLengthDisplay] = useState(0);
//---------- changes
useEffect(() => {
setSeasonDisplay(data["lastname"])
setSeasonLengthDisplay (2)
}, [data])
//----------------
return (
<View style={styles.container}>
<View style={styles.innerContainer}>
<DropDownChoice whichDropDown="Season" HowMany={seasonlength} ItemChoices={seasondisplay} />
<DropDownChoice whichDropDown="Veggies" HowMany={4} ItemChoices={["carrots", "turnip", "peas", "corn"]} />
<DropDownChoice whichDropDown="Fruit" HowMany={3} ItemChoices={["apples", "oranges", "bananas"]} />
<Button
title="Connect to DB"
onPress={() => {
console.log("please work");
console.log(data);
handleClick();
}}

Related

how to prevent keep getting duplicated result from API with Flatlist react native?

im trying to get repos in order of stars from the API, but as i scroll, i keep getting random duplicates :
export default function App() {
const [repos, setRepos] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(true);
useEffect(() => {
const getUsers = async () => {
const res = await axios.get(
`https://api.github.com/search/repositories?q=created:>2020-01-01&sort=stars&order=desc&page=${page}`
);
const data = await res.data;
// setRepos([...repos, ...data.items]);
setRepos([...repos, ...data.items]);
setLoading(false);
};
getUsers();
}, [page]);
const scrollToEnd = () => {
if (loading === false) {
setPage(page + 1);
setLoading(true);
}
console.log("page", page);
};
const renderItem = ({ item }) => (
<Text className="boxed">
Stars: {item.stargazers_count} Id: {item.id}
</Text>
);
return (
<SafeAreaView style={styles.screen}>
<View style={styles.container}>
<FlatList
data={repos}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onEndReached={scrollToEnd}
showsVerticalScrollIndicator={false}
/>
{loading && <Text className="loading">...loading</Text>}
</View>
<StatusBar style="auto" />
</SafeAreaView>
);
}
You should avoid duplicate data before set it to state, update your getUsers function with this,
useEffect(() => {
const getUsers = async () => {
const res = await Axios.get(
`https://api.github.com/search/repositories?q=created:>2020-01-01&sort=stars&order=desc&page=${page}`,
);
const data = await res.data;
let arrRepos = [...repos, ...data.items];
let uniq = new Set(arrRepos.map((objRepo) => JSON.stringify(objRepo)));
arrRepos = Array.from(uniq).map((objRepo) => JSON.parse(objRepo));
setRepos(arrRepos);
setLoading(false);
};
getUsers();
}, [page]);
I think you got too much going on in your useEffect function, maybe missing a closing }. Try this:
function App() {
const [repos, setRepos] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(true);
// This will trigger when page changes:
useEffect(() => {
getUsers();
}, [page]);
const getUsers = async () => {
const res = await axios.get(
`https://api.github.com/search/repositories?q=created:>2020-01-01&sort=stars&order=desc&page=${page}`
);
const data = await res.data;
// setRepos([...repos, ...data.items]);
setRepos([...repos, ...data.items]);
setLoading(false);
};
const scrollToEnd = () => {
if (loading === false) {
setPage(page + 1);
setLoading(true);
}
console.log("page", page);
};
const renderItem = ({ item }) => (
<Text className="boxed">
Stars: {item.stargazers_count} Id: {item.id}
</Text>
);
return (
<SafeAreaView style={styles.screen}>
<View style={styles.container}>
<FlatList
data={repos}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onEndReached={scrollToEnd}
showsVerticalScrollIndicator={false}
/>
{loading && <Text className="loading">...loading</Text>}
</View>
<StatusBar style="auto" />
</SafeAreaView>
);
}
export default App;

Show ActivityIndicator in React Native?

I have a function to fetch items from an API that is inside UseEffect. And i'm looking to call this function every time the status of the selectedItem or the items changes and show an ActivityIndicator before the function returns the result. The ActivityIndicator appears when the items are uploading but not when the status of the selectedItem changes ?
I have my code like this :
export default () => {
const [items, setItems] = useState();
const [selectedItem, setSelectedItem] = useState(null);
const [isLoading, setLoading] = useState(true);
const getItems = () => {
get('/api/items').then((rep) => {
setItems(rep);
setLoading(false);
}
});
};
useEffect(() => {
getItems();
}, [selectedItem.status]);
return (
<SafeAreaView style={styles.container}>
{isLoading ? (
<View style={[styles.spinnerContainer, styles.horizontal]}>
<ActivityIndicator />
</View>
) : ((items !== [])
&& (
<SectionList
stickySectionHeadersEnabled={false}
style={{ paddingHorizontal: 20, }}
sections={items}
refreshing={isLoading}
keyExtractor={(item, index) => item + index}
...
/>
))}
</SafeAreaView>
);
};
You can try setLoading(true) inside getItems
const getItems = () => {
setLoading(true);
get('/api/items').then((rep) => {
setItems(rep);
setLoading(false);
});
};

React Native API call

How do I display results from the API call in code lower in the page?
The {data.payrollid} after Complete is not showing any value. ie: the text only shows 'Complete' with no value after it.
My returned JSON looks like this...
{"status_code":200,"payrollid":10,"message":"Success"}
When I console.log(data) I can see that the fetch worked and I can see my JSON array.
Below is my React Native code
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://www.mywebsite.ca/api/test.php')
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
return (
<>
<View>
{isLoading ? <Text>Loading...</Text> : <Text>Complete {data.payrollid}</Text> }
</View>
<View style={styles.container}>
<Text>This is my new app.</Text>
<Text>Some text</Text>
<StatusBar style="auto" />
</View>
</>
);
Your code should look something like this:
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState({});
useEffect(() => {
fetch('https://www.mywebsite.ca/api/test.php')
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
return (
<>
<View>
{isLoading ? <Text>Loading...</Text> :
<Text>Complete{data.payrollid}.</Text>
}
</View>
<View style={styles.container}>
<Text>This is my new app.</Text>
<Text>Your Text</Text>
<StatusBar style="auto" />
</View>
</>
);
You need to save your data in your data state.
const [data, setData] = useState({});
useEffect(() => {
fetch('https://www.mywebsite.ca/api/test.php')
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
And since your getting an object switch your original state to an object.

React Native scroll to refresh is not working properly with pagination

i am new to react native please bear with me. I currently have a flatlist in my react native project which fetch's posts from my backend using an api. I have success implemented pagination on my backend and in my app when i scroll to the end it adds the newly fetched posts to the bottom of the current posts shown.
My problem is when i scroll to refresh, i only want the posts of page 0 to get shown.
What is happening is the posts on page 0 get added to the bottom of the posts instead of showing these posts only.
So for example, when i load my app, posts on page 0 get rendered and when i scroll to refresh instead of just showing posts on page 0, the posts on page 0 get added to the bottom of the posts on page 0 and therefore i get an error that id is duplicated.
The required action i am trying to obtain is when i scroll to refresh i just want the posts on page 0 to show.
Here is my code:
function PostsScreen({ navigation }) {
const [posts, setPosts] = useState([]);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const loadPosts = async () => {
setLoading(true);
const response = await postsApi.getPosts(page);
setLoading(false);
if (!response.ok) return setError(true);
setError(false);
setPosts([...posts,...response.data]);
};
const[page,setPage]=useState(0);
useEffect(() => {
loadPosts(page);
}, [page]);
const handleLoadMore = ()=>{
console.log('loadmore')
setPage(page+1);
}
const [refreshing, setRefreshing] = useState(false);
return (
<>
<ActivityIndicator visible={loading} />
<Screen style={styles.screen}>
<FlatList
data={posts} // to have all the data
keyExtractor={(post) => post.id.toString()}
renderItem={({ item,index }) => (
<Card
title={item.title}
subTitle={item.subTitle}
onPress={() => navigation.navigate(routes.POST_DETAILS, {post:item,index})}
/>
)}
refreshing={refreshing}
onRefresh={() => {
loadPosts(0); // I think the problem is here.//
setPage(0);
}}
onEndReached={handleLoadMore}
onEndReachedThreshold={0}
initialNumToRender={10}
/>
</Screen>
</>
);
}
Thank you in advance.
From what I read, I believe you want to implement pagination in your application and you have that in your code as well. Now, when you try refreshing your list, you just want to set the list to the initial 10 items. if so,
function PostsScreen({ navigation }) {
const [posts, setPosts] = useState([]);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(0);
const [refreshing, setRefreshing] = useState(false);
const loadPosts = async () => {
setLoading(true);
const response = await postsApi.getPosts(page);
setLoading(false);
if (refreshing)
setRefreshing(false);
if (!response.ok) return setError(true);
setError(false);
if (page == 0)
setPosts(response.data)
else
setPosts([...posts, ...response.data]);
};
useEffect(() => {
loadPosts();
}, [page]);
const handleLoadMore = () => {
setPage(page + 1);
};
const onRefresh = () => {
setRefreshing(true);
setPage(0);
};
return (
<>
<ActivityIndicator visible={loading} />
<Screen style={styles.screen}>
<FlatList
data={posts}
renderItem={({ item, index }) => (
<Card
title={item.title}
subTitle={item.subTitle}
onPress={() => navigation.navigate(routes.POST_DETAILS, { post: item, index })}
/>
)}
refreshing={refreshing}
onRefresh={onRefresh}
onEndReached={handleLoadMore}
onEndReachedThreshold={0.05}
keyExtractor={(post) => post.id.toString()}
/>
</Screen>
</>
);
}
This should fix your problem.
const loadPosts = async () => {
setLoading(true);
const response = await postsApi.getPosts(page);
setLoading(false);
if (!response.ok) return setError(true);
setError(false);
if(page=== 0){
setPosts(response.data);
}else{
setPosts([...posts,...response.data]);
}
};
//...
onRefresh={() => {
setPage(0);
loadPosts(); // loadPosts takes no argument.
}
}
//...
const handleLoadMore = ()=>{
console.log('loadmore')
setPage(page+1);
loadPosts();
}

onEndReached function calls several times in FlatList(react-native)

In my code, for the first time, loadAlbums method runs correctly. Why I scroll down retrieveMore function calls several times.
const PAGE_SIZE = 15;
const App = () => {
const [albums, setAlbums] = useState([]);
const [page, setPage] = useState(1);
const loadAlbums = async () => {
const response = await axios.get(`http://xxx:3000/posts/${PAGE_SIZE}/${page}`);
setAlbums([...albums, ...response.data]);
setPage(page + 1)
}
useEffect(() => {
loadAlbums();
}, [])
const renderRowItem = ({ item }) => {
return (
<View style={styles.item}>
<Text>{item.id}</Text>
<Text>{item.body}</Text>
</View>
)
}
const retrieveMore = useCallback(() => {
loadAlbums(); // this line calls several times
})
return (
<View style={styles.screen}>
<FlatList
data={albums}
renderItem={renderRowItem}
keyExtractor={(item, index) => index.toString()}
onEndReached={retrieveMore}
onEndReachedThreshold={0.5}
/>
</View>
)
}
Just Reduce threshold
onEndReachedThreshold={0.5} to onEndReachedThreshold={0.2}
Currently what's happening here every time you reach 50% of the screen the api will be invoked.