State is not updating with my data fetched from an API - react-native

I am trying to get data from a endpoint and then output this data, I can successfully get my data fetched and display it immediately using
fetch(strURL)
.then(res => res.json())
.then((result) => {
console.log("grabed data "+ JSON.stringify(result));
But whenever I try to set the data to my State variable response it fails to do so resulting in an undefined variable. I am really confused why this is occurring as I have another screen that fetches a the same endpoint but it doesn't seem to save to the state variable.
This is the data which is returned:
[{"act_id":"1","act_name":"A bush adventure trail ride in the blue mountains","cat_sub_category":"Free riding","bus_name":"Ditch the road"},{"act_id":"2","act_name":"Paddock riding in the ditch","cat_sub_category":"stable riding","bus_name":"Hallam horses"}]
This is the working screen which fetches the data above
function ActivityDemo(props) {
let [isLoading, setIsLoading] = useState(true);
let [error,setError] = useState();
let [response,setResponse] = useState();
useEffect(() =>{
console.log("fetch activities");
fetch("https://domain/api/activities.php")
.then(res => res.json())
.then((result) => {
console.log("grabed data "+ result);
setIsLoading(false);
setResponse(result);
},
(error) => {
setIsLoading(true);
setError(error);
console.error("error ")
})
}, []);
//What renders
const renderItem = ({item}) => (
<ActivityWidget item={item} ></ActivityWidget>
);
//determines what is displayed
const getContent = (navigation) => {
if (isLoading == true){
return <ActivityIndicator size="large"></ActivityIndicator>
}
if(error == true){
return <Text>{error}</Text>
}
if(isLoading ==false){
console.log(response);
return (
<FlatList
data={response}
renderItem={renderItem}
keyExtractor={item => item.act_id}
/>
);
}
}
return(
<View style={[ContainerStyle.Center]}>
{getContent()}
</View>
);
}
This snippet is from the screen which doesn't work
function ActivityDetails({route},props) {
//get the Route variables
const {actId} = route.params;
let [isLoading, setIsLoading] = useState(true);
let [error, setError] = useState();
let [response, setResponse] = useState();
let strURL = "https://domain/api/detailedActivity.php?actId="+actId;
useEffect(() =>{
console.log("fetch detailed data!");
console.log(strURL);
fetch("https://domain/api/activities.php")
.then(res => res.json())
.then((result) => {
console.log("grabed data "+ JSON.stringify(result));
setIsLoading(false);
setResponse(result);
},
(error) => {
setIsLoading(true);
setError(error);
console.error("error "+ error)
})
}, []);
//determines what is displayed
const getContent = () => {
if (isLoading == true){
return <ActivityIndicator size="large"></ActivityIndicator>;
}
if(isLoading == false){
console.log("Load response Data " +response);
return(
<View style={[ContainerStyle.Container]} >
<Text>{"Name: "+response[0].act_name}</Text>
<Text>{"Description: " +response[0].act_description}</Text>
<Text>{"Bussiness: "+response[0].act_name}</Text>
<Text>{"Category: "+response[0].act_name}</Text>
</View>
);
}
if (error == true){
return <Text>{error}</Text>
}
}
return(
<View style={[ContainerStyle.Center]}>
{getContent()}
</View>
);
}
This is the console logs
LOG fetch activities
LOG grabed data [object Object],[object Object]
LOG undefined
LOG [{"act_id": "1", "act_name": "A bush adventure trail ride in the blue mountains", "bus_name": "Ditch the road", "cat_sub_category": "Free riding"}, {"act_id": "2", "act_name": "Paddock riding in the ditch", "bus_name": "Hallam horses", "cat_sub_category": "stable riding"}]
LOG fetch detailed data!
LOG https://domain/api/detailedActivity.php?actId=2
LOG grabed data [{"act_id":"1","act_name":"A bush adventure trail ride in the blue mountains","cat_sub_category":"Free riding","bus_name":"Ditch the road"},{"act_id":"2","act_name":"Paddock riding in the ditch","cat_sub_category":"stable riding","bus_name":"Hallam horses"}]
LOG Load response Data undefined
LOG Load response Data undefined
I'm not sure why this is not working If someone could tell me what I am doing wrong? Is it to do with the way I am setting state?
Thank you,
Andrew
edit:
I've hidden the endpoints domain

Try this way. Create response state like below.
const [response,setResponse] = useState([]);
then assign value like below
setResponse([...result]);

I have fixed the issue by converting my functions to classes and formatting it with a ComponentDidMount.
class ActivityInfo extends Component {
state = { response : [], isLoading : true};
componentDidMount(){
const {navigation, route}=this.props
const { actId } = route.params;
console.log(actId);
const res = fetch("https://domain/api/detailedActivity.php?actId="+ actId)
.then(res => res.json())
.then((result) => {
console.log("grabbed data "+ result);
this.setState({isLoading:false,response:result});
},
(error) => {
setIsLoading(true);
setError(error);
console.error("error ")
})
console.log(this.state.response)
}
render(){
if(this.state.isLoading == true){
return(<ActivityIndicator style={{alignSelf:"center"}} size="large"></ActivityIndicator>)
}
else{
return(
<View style={[ContainerStyle.Container]} >
<Text>{"Name: "+this.state.response[0].act_name}</Text>
<Text>{"Description: " +this.state.response[0].act_description}</Text>
<Text>{"Bussiness: "+this.state.response[0].bus_name}</Text>
<Text>{"Category: "+this.state.response[0].act_id}</Text>
</View>
);
}
}
}

Related

Flatlist is very slow in using big data in react native

i have a big data list of products thats paginate, in every page it load 10 item, but when i add new items to itemlist,flatlist gets very slow,As the number of pages increases, so does the loading time of new products,The function of the choose button is also slowed down.
How to speed up loading I tried all the best methods but it still did not work. Did not React Native really solve this problem?
export default function Products(props) {
const toast = useToast();
const [isLoading, setSetIsLoading] = useState(true);
const [items, setItems] = useState([]);
const [fetchStatus, setFetchStatus] = useState(false);
const [page, setPage] = useState(1);
const [sending, setSending] = useState(false);
async function getProducts() {
let token = await AsyncStorage.getItem('#token');
let data = {
token: token,
page: page,
};
await get_products(data)
.then(res => {
setItems([...items, ...res.data.data.docs]);
setPage(res.data.data.nextPage);
})
.catch(err => {
console.log(err);
});
}
async function getNextPage() {
let token = await AsyncStorage.getItem('#token');
let data = {
token: token,
page: page,
};
await get_products(data)
.then(res => {
setItems([...items, ...res.data.data.docs]);
setPage(res.data.data.nextPage);
})
.catch(err => {
console.log(err);
});
}
async function selectProduct(id) {
setSending(true);
console.log({id});
let token = await AsyncStorage.getItem('#token');
let data = {
product_id: id
};
await select_products(data,token).then(res => {
toast.show({
description:res.data.message
})
setSending(false);
}).catch(rej => {
console.log({rej})
toast.show({
description:rej?.response?.data.message,
})
setSending(false);
})
}
useFocusEffect(
React.useCallback(() => {
getProducts();
return () => {
setItems([]);
setPage();
};
}, []),
);
renderItem =({item}) => (
<Card
selectProduct={id => selectProduct(id)}
sending={sending}
obj={item}
/>
)
return (
<View mb={20}>
<FlatList
data={items}
extraData={items}
removeClippedSubviews={true}
renderItem={renderItem}
keyExtractor={(item) => `${item._id}-item`}
onEndReached={getNextPage}
maxToRenderPerBatch="13"
ListFooterComponent={() => {
return <ActivityIndicator color="orange" size="large" />;
}}></FlatList>
</View>
);
}
Did you use **map method **?
It can help you for more easily loading data

React Native useState Render Error : [Too many re-renders.]

This is my code:
export default function App() {
const [onProcess, setOnProcess] = useState("normal")
var myid = "123"
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://api.npoint.io/0294bea2185268c9ac70')
.then((response) => response.json())
.then((json) => setData(json))
.catch((error) => console.log('ERR :', error))
},[]);
for (let x in data) {
if (data[x].client_id == myid) {
var set = data[x].situation
setOnProcess(set)
console.log(data[x].situation)
break
}
}
const rt_normal = (
<View style={styles.container}>
<Text> This is normal view </Text>
</View>
)
const rt_process = (
<View style={styles.container}>
<Text> This is process view </Text>
</View>
)
if (onProcess == "normal") {
return rt_normal
}
else if (onProcess == "_on_process") {
return rt_process
}
}
The error I got is:
:[Render Error. Too many re-renders. React limits the number of renders to prevent an infinite loop.]
This happens because of setOnProcess(set) code. How can I solve this?
You should remove your for...in loop and refactor to utilise useEffect.
useEffect(() => {
// Get a specific entry where client_id matches myId.
const filteredItem = data.find(item => item.client_id === myId);
// Perform a check as .find() can return undefined.
if(filteredItem.situation) {
setSituation(filteredItem.situation);
}
}, [data]);
Put the for loop inside a useEffect
(Untested) example:
useEffect(() => {
for (let x in data) {
if (data[x].client_id == myid) {
var set = data[x].situation;
setOnProcess(set);
console.log(data[x].situation);
break;
}
}
}, [data]);

react native - why is my console.log returning [] but items get rendered on screen?

I am trying to access the object obtained from my API get request but I keep getting Array[] returned in the console.log while the items get rendered on the screen.
Can someone spot where I went wrong?
const [posts, setPosts] = useState([]);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const loadPosts = async () => {
setLoading(true);
const response = await messagesApi.getMessages();
setLoading(false);
if (refreshing) setRefreshing(false);
if (!response.ok) return setError(true);
setError(false);
setPosts(response.data);
};
useEffect(() => {
const newsocket = io.connect("http://ip:port");
loadPosts();
console.log(posts); // not working
newsocket.on("connect", (msg) => {
setSocket(newsocket);
});
return () => newsocket.close();
}, []);
return (
<FlatList
data={posts}
keyExtractor={(post) => post.id.toString()}
renderItem={({ item, index }) => (
<MessagesList
title={item.title}
onPress={() =>
navigation.navigate(routes.CHAT, { message: item, index, updateView })
}
/>
)}
/>
);
ISSUE
console.log executes before getting an API response.
SOLUTION
console.log would work when you add posts in dependency like
useEffect(() => {
console.log(posts);
}, [posts]); // added posts here

fetching dynamic data and showing in picker gives me error

constructor(){
super()
this.state = {
make :[]
}
}
componentWillMount(){
fetch('http://link.com', {
method: 'GET',
}).then((response) => response.json())
.then((responseJson) => {
if(responseJson.message === "List of Years"){
var length = responseJson.result.length.toString();
let newarray=[];
for(var i = 0 ; i < length ; i++){
// newarray.push(responseJson.result[i].Makes.year_name.toString());
this.setState({
make: [...this.state.make, responseJson.result[i].Makes.year_name]
});
}
//this.setState({make : newarray})
}
}
).catch((error) => {
console.error(error);
});
}
<Picker
mode="dropdown"
selectedValue={this.state.selected}
onValueChange={(value) => this.setState({selected: value})}>
{this.state.make.map((item, index) => {
return (<Item label={item} value={index}/>)
})}
</Picker>
I made a state named make in construtor and then in componentWillMount() function i fetched data from server and then it set state to make array.But it looks like there is problem with my code.It throws error:
TypeError:undefined is not an object(evaluating 'child.props.value')
I also tried with but problem remains.What i am doing wrong here?

React Native Flat List doesn't call onEndReached handler after two successful calls

I implement a very simple list that calls a server that returns a page containing books.Each book has a title, author, id, numberOfPages, and price). I use a Flat List in order to have infinite scrolling and it does its job very well two times in a row (it loads the first three pages) but later it doesn't trigger the handler anymore.
Initially it worked very well by fetching all available pages, but it stopped working properly after I added that extra check in local storage. If a page is available in local storage and it has been there no longer than 5 seconds I don't fetch the data from the server, instead I use the page that is cached. Of course, if there is no available page or it is too old I fetch it from the server and after I save it in local storage.(Something went wrong after adding this behavior related to local storage.)
Here is my component:
export class BooksList extends Component {
constructor(props) {
super(props);
this.state = {
pageNumber: 0
};
}
async storePage(page, currentTime) {
try {
page.currentTime = currentTime;
await AsyncStorage.setItem(`page${page.page}`, JSON.stringify(page));
} catch (error) {
console.log(error);
}
}
subscribeToStore = () => {
const { store } = this.props;
this.unsubsribe = store.subscribe(() => {
try {
const { isLoading, page, issue } = store.getState().books;
if (!issue && !isLoading && page) {
this.setState({
isLoading,
books: (this.state.books ?
this.state.books.concat(page.content) :
page.content),
issue
}, () => this.storePage(page, new Date()));
}
} catch (error) {
console.log(error);
}
});
}
componentDidMount() {
this.subscribeToStore();
// this.getBooks();
this.loadNextPage();
}
componentWillUnmount() {
this.unsubsribe();
}
loadNextPage = () => {
this.setState({ pageNumber: this.state.pageNumber + 1 },
async () => {
let localPage = await AsyncStorage.getItem(`page${this.state.pageNumber}`);
let pageParsed = JSON.parse(localPage);
if (localPage && (new Date().getTime() - localPage.currentTime) < 5000) {
this.setState({
books: (
this.state.books ?
this.state.books.concat(pageParsed.content) :
page.content),
isLoading: false,
issue: null
});
} else {
const { token, store } = this.props;
store.dispatch(fetchBooks(token, this.state.pageNumber));
}
});
}
render() {
const { isLoading, issue, books } = this.state;
return (
<View style={{ flex: 1 }}>
<ActivityIndicator animating={isLoading} size='large' />
{issue && <Text>issue</Text>}
{books && <FlatList
data={books}
keyExtractor={book => book.id.toString()}
renderItem={this.renderItem}
renderItem={({ item }) => (
<BookView key={item.id} title={item.title} author={item.author}
pagesNumber={item.pagesNumber} />
)}
onEndReachedThreshold={0}
onEndReached={this.loadNextPage}
/>}
</View>
)
}
}
In the beginning the pageNumber available in the state of the component is 0, so the first time when I load the first page from the server it will be incremented before the rest call.
And here is the action fetchBooks(token, pageNumber):
export const fetchBooks = (token, pageNumber) => dispatch => {
dispatch({ type: LOAD_STARTED });
fetch(`${httpApiUrl}/books?pageNumber=${pageNumber}`, {
headers: {
'Authorization': token
}
})
.then(page => page.json())
.then(pageJson => dispatch({ type: LOAD_SUCCEDED, payload: pageJson }))
.catch(issue => dispatch({ type: LOAD_FAILED, issue }));
}
Thank you!