Hi I have very common problem, react native application freezes while fetching API,
It is like when you fetching you cannot click or do anythink till your fetching ends.
This is my code where I call function which fetchs APi
import React, { useEffect, useState } from "react";
import { StyleSheet, View, FlatList } from "react-native";
// Api`s
import { topCategory } from "../../api/appModels/category";
import { banners } from "../../api/appModels/slider";
import { dinamicBlocks } from "../../api/appModels/product";
// Hooks
import useApi from "../../Hooks/useApi";
// UI-Components
import Container from "../../uicomponents/General/Container";
import BannerSlider from "../../uicomponents/Slider/BannerSlider";
import Screen from "../../uicomponents/General/Screen";
import TopProductPattern from "../../uicomponents/Category/pattern/TopProductPattern";
import SliderPlaceholder from "../../uicomponents/Skeleton/Sliders/SliderPlaceholder";
import CategoryAvatarPlaceholder from "../../uicomponents/Skeleton/Category/CategoryAvatarPlaceholder";
import CategoryBlocks from "../../uicomponents/Product/blocks/CategoryBlocks";
import Header from "../../uicomponents/header/Header";
import ActivityIndicator from "../../uicomponents/Loaders/ActivityIndicator";
const HomeScreen = () => {
const {
data: topCategoryList,
loading: topCategoryLoading,
request: categoryRequest,
} = useApi(topCategory);
const {
data: bannersList,
loading: bannerLoading,
request: bannersRequest,
} = useApi(banners);
const {
loading: blocksLoading,
request: blocksRequest,
} = useApi(dinamicBlocks);
const [blocks, setBlocks] = useState([]);
const [indexing, setIndexing] = useState(1);
const [countBlocks, setCountBlocks] = useState(0);
const [loader, setLoader] = useState(false);
useEffect(() => {
// Calling Api`s
categoryRequest();
bannersRequest();
blocksRequest((item) => {
setIndexing(indexing + 1);
setBlocks(item["blocks"]);
setCountBlocks(item["count"]);
}, 1);
}, []);
const loadMore = () => {
if (!blocksLoading) {
blocksRequest((item) => {
setIndexing(indexing + 1);
setBlocks(blocks.concat(item["blocks"]));
console.log(item);
}, indexing);
setLoader(indexing != countBlocks);
}
};
return (
<Screen>
<FlatList
data={blocks}
keyExtractor={(item) => item.categories.toString()}
ListHeaderComponent={
<>
<Header />
{bannerLoading ? (
<SliderPlaceholder />
) : (
<BannerSlider data={bannersList} dots />
)}
<Container>
<View style={{ paddingTop: 10 }}>
{topCategoryLoading ? (
<CategoryAvatarPlaceholder />
) : (
<TopProductPattern data={topCategoryList} />
)}
</View>
</Container>
</>
}
ListFooterComponent={<ActivityIndicator visible={loader} />}
renderItem={({ item }) => (
<CategoryBlocks title={item.blocks_name} data={item.categoriesList} />
)}
onEndReached={loadMore}
onEndReachedThreshold={0.1}
/>
</Screen>
);
};
export default HomeScreen;
const styles = StyleSheet.create({});
Here is my code which fetches API
import { useState } from "react";
const useApi = (apiFunc) => {
const [data, setData] = useState([]);
const [error, setError] = useState("");
const [loading, setLoading] = useState(true);
const request = async (callBack = () => {}, ...args) => {
setLoading(true);
const response = await apiFunc(...args);
if (!response.ok) return setError(response.problem);
setLoading(false);
setError("");
setData(response.data);
if (response.ok) {
callBack(response.data);
}
};
return {
data,
error,
loading,
request,
setLoading,
setData,
};
};
export default useApi;
I think there is problem with RN-bridge HELP ME PLEASE !
Probably it's happened because you set onEndReachedThreshold={0.1} and when data was loaded one more time request will be send and it's make problem.
So you can increase this value for example to 0.7.
In the official react native website This is how it is explained onEndReachedThreshold:
"How far from the end (in units of visible length of the list) the bottom edge of the list must be from the end of the content to trigger the onEndReached callback. Thus a value of 0.5 will trigger onEndReached when the end of the content is within half the visible length of the list."
Related
i'm trying to implement pdf viewer from url stored in firestore in react js
how i can get item.docId in setPdfUrls please help me out i'm new to react js and web development
Where I'm stuck is that I don't understand how to do it please help
How to get item.docId so that i can get url for pdf in firestore
`
import React, { useState, useEffect, useContext } from "react";
import { Card, Header, Player } from "../components";
import * as ROUTES from "../constants/routes";
import { FirebaseContext } from "../context/firebase";
import { ref, getDownloadURL } from "firebase/storage";
import { storage } from "../lib/firebase.prod";
import { SelectProfileContainer } from "./profiles";
import { FooterContainer } from "./footer";
export function BrowseContainer({ slides }) {
var [pdfUrls, setPdfUrls] = useState([]);
const [resume, setResume]=useState(null);
useEffect(()=>{
getDownloadURL(ref(storage, 'Resume.pdf')).then((url)=>{
setResume(url);
})
},[]);
const [category, setCategory] = useState("articles");
const [profile, setProfile] = useState({});
const [loading, setLoading] = useState(true);
const [slideRows, setSlideRows] = useState([]);
const { firebase } = useContext(FirebaseContext);
const user = firebase.auth().currentUser || {};
useEffect(() => {
setTimeout(() => {
setLoading(false);
}, 3000);
}, [profile.displayName]);
useEffect(() => {
setSlideRows(slides[category]);
}, [slides, category]);
return profile.displayName ? (
<>
<Card.Group>
{slideRows.map((slideItem) => (
<Card key={`${category}-${slideItem.title.toLowerCase()}`}>
<Card.Title>{slideItem.title}</Card.Title>
<Card.Entities>
{slideItem.data.map((item) => (
<Card.Item key={item.docId} item={item}>
<Card.Meta>
<Card.SubTitle>{item.title}</Card.SubTitle>
<br/>
<br/>
</Card.Meta>
<Card.Image
src={item.image} alt={item.title}/>
</Card.Item>
))}
</Card.Entities>
<Card.Feature category={category}>
<Player>
<Player.Button />
<Player.Video src={resume} />
</Player>
</Card.Feature>
</Card>
))}
</Card.Group>
<FooterContainer />
</>
) : (
<SelectProfileContainer user={user} setProfile={setProfile} />
);
}
`
I'm building a React component that shows data on an Order Summary Screen to conclude the order process for my App.
I am receiving the message:
Warning: Each child in a list should have a unique "key" prop.%s%s See...
Here is the complete error for reference:
Check the render method of `SummaryOrder`., ,
in RCTView (at SummaryOrder.js:24)
in SummaryOrder (at PreOrderScreen.js:111)
in ScrollView (at PreOrderScreen.js:105)
in RCTView (at PreOrderScreen.js:99)
Location on errors are also marked with "=>".
SummaryOrder.js:
import React from "react";
import { View, StyleSheet } from "react-native";
//Number
import NumberFormat from "../../../components/UI/NumberFormat";
//PreOrderItem
import PreOrderItem from "./PreOrderItem";
//Text
import CustomText from "../../../components/UI/CustomText";
import Colors from "../../../utils/Colors";
//PropTypes check
import PropTypes from "prop-types";
export class SummaryOrder extends React.PureComponent {
render() {
const { cartItems, total } = this.props;
return (
in RCTView (at SummaryOrder.js:24) => ***<View style={styles.container}>***
<CustomText style={{ ...styles.title, marginVertical: 5 }}>
Order Summary
</CustomText>
<View style={{ backgroundColor: "#fff", paddingHorizontal: 10 }}>
{cartItems.map((item) => {
return (
in SummaryOrder (at PreOrderScreen.js:111) => ****<View key={item.item.createdAt}>****
<PreOrderItem item={item} />
</View>
);
})}
</View>
<View style={styles.total}>
<CustomText
style={{
fontSize: 15,
color: Colors.text,
fontWeight: "500",
}}
>
Total
</CustomText>
<NumberFormat price={total.toString()} />
</View>
</View>
);
}
}
SummaryOrder.propTypes = {
cartItems: PropTypes.array.isRequired,
total: PropTypes.number.isRequired,
};
PreOrderScreen.js:
import React, { useState, useEffect, useRef } from "react";
import { useIsFocused } from "#react-navigation/native";
import { View, StyleSheet, ScrollView } from "react-native";
//Address
import Address from "./components/Address";
//Redux
import { useSelector } from "react-redux";
//Steps
import Colors from "../../utils/Colors";
import { Header, SummaryOrder, TotalButton, UserForm } from "./components";
import Loader from "../../components/Loaders/Loader";
export const PreOrderScreen = (props) => {
const unmounted = useRef(false);
const isFocused = useIsFocused();
const [loading, setLoading] = useState(true);
const carts = useSelector((state) => state.cart.cartItems);
const { cartItems, total, cartId } = props.route.params;
const [error, setError] = useState("");
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [address, setAddress] = useState("");
const [province, setProvince] = useState("");
const [town, setTown] = useState("");
useEffect(() => {
return () => {
unmounted.current = true;
};
}, []);
useEffect(() => {
if (isFocused) {
setLoading(true);
const interval = setInterval(() => {
setLoading(false);
}, 1000);
return () => clearInterval(interval);
}
return;
}, [isFocused]);
const getInfo = (province, town) => {
setProvince(province);
setTown(town);
};
const getReceiver = (name, phone, address) => {
setName(name);
setPhone(phone);
setAddress(address);
};
const checkValidation = (error) => {
setError(error);
};
let orderItems = [];
cartItems.map((item) => {
orderItems.push({ item: item.item._id, quantity: item.quantity });
});
const fullAddress = `${address}, ${town} ,${province}`;
const toPayment = async () => {
try {
if (error == undefined && province.length !== 0 && town.length !== 0) {
props.navigation.navigate("Payment", {
screen: "PaymentScreen",
params: {
fullAddress,
orderItems,
name,
phone,
total,
cartId,
carts,
},
});
} else {
alert("Please enter your full information.");
}
} catch (err) {
throw err;
}
props.navigation.navigate("Payment", {
screen: "PaymentScreen",
params: {
fullAddress,
orderItems,
name,
phone,
total,
cartId,
carts,
},
});
};
useEffect(() => {
if (carts.items.length === 0) {
props.navigation.goBack();
}
}, [carts.items]);
return (
in RCTView (at PreOrderScreen.js:99) => ***<View style={styles.container}>***
<Header navigation={props.navigation} />
{loading ? (
<Loader />
) : (
<>
<ScrollView>
<UserForm
getReceiver={getReceiver}
checkValidation={checkValidation}
/>
<Address getInfo={getInfo} />
in ScrollView (at PreOrderScreen.js:105) =>
***<SummaryOrder cartItems={cartItems} total={total} />***
</ScrollView>
<TotalButton toPayment={toPayment} />
</>
)}
</View>
);
};
Thanks for your help in advance!
The createdAt values are not unique for them. The quick fix is to use index instead, but if you plan to do any removing or sorting, you should rather use some sort of unique identifier for the items (phone, email, user ID, etc). I only skimmed your code, but you could use phone instead.
The keys are important because it hints to React when it needs to update the list. The docs are helpful https://reactjs.org/docs/lists-and-keys.html#keys for more on this.
Anyway, the quick fix is shown here. Read https://stackoverflow.com/a/43642421 as to why you shouldn't do this. I'll leave a better implementation up to you.
...
{ cartItems.map((item, index) => {
return (
<View key={index}>
<PreOrderItem item={item} />
</View> );
})
}
...
whenever you use map in react, you should define a unique key for the repeating tag or component, this is for react to keep track of changes in the array you are mapping on, and if any changes occur in array, react will know where to re-render the related tag or component and leave others alone :)
if you don't have a plan to re-order the array, you can simply use index as key but if you want to re-order the array elements, it's better if first you define a unique id for them(if it's not provided by server) and then do the map the array
How to pass the {requests} prop to the RequestRow component after executing the setRequests? My understanding is that the requests get initialized as undefined in the beginning and before being set with the asynchronously called object, it gets passed to the RequestRow component as undefined, and the error occurs.
import React, { useState, useEffect } from 'react';
import 'semantic-ui-css/semantic.min.css';
import Layout from '../../../components/Layout';
import { Button } from 'semantic-ui-react';
import { Link } from '../../../routes';
import Campaign from '../../../blockchain/campaign';
import { Table } from 'semantic-ui-react';
import RequestRow from '../../../components/RequestRow';
const RequestsIndex = ({ address }) => {
const { Header, Row, HeaderCell, Body } = Table;
const campaign = Campaign(address);
const [requestCount, setRequestCount] = useState();
const [requests, setRequests] = useState([]);
const getRequests = async () => {
const count = await campaign.methods.getRequestsCount().call();
setRequestCount(count);
};
let r;
const req = async () => {
r = await Promise.all(
Array(parseInt(requestCount))
.fill()
.map((_element, index) => {
return campaign.methods.requests(index).call();
})
);
setRequests(r);
};
useEffect(() => {
getRequests();
if (requestCount) {
req();
}
}, [requestCount]);
return (
<Layout>
<h3>Requests List.</h3>
<Link route={`/campaigns/${address}/requests/new`}>
<a>
<Button primary>Add Request</Button>
</a>
</Link>
<Table>
<Header>
<Row>
<HeaderCell>ID</HeaderCell>
<HeaderCell>Description</HeaderCell>
<HeaderCell>Amount</HeaderCell>
<HeaderCell>Recipient</HeaderCell>
<HeaderCell>Approval Count</HeaderCell>
<HeaderCell>Approve</HeaderCell>
<HeaderCell>Finalize</HeaderCell>
</Row>
</Header>
<Body>
<Row>
<RequestRow requests={requests}></RequestRow>
</Row>
</Body>
</Table>
</Layout>
);
};
export async function getServerSideProps(context) {
const address = context.query.address;
return {
props: { address },
};
}
export default RequestsIndex;
The RequestRow component is shown below. It takes in the {requests} props, which unfortunately is undefined.
const RequestRow = ({ requests }) => {
return requests.map((request, index) => {
return (
<>
<div>Request!!!</div>
</>
);
});
};
export default RequestRow;
The snapshot of the error is shown below:
I think React is trying to render your component before your promises resolve. If that's the case, all you need to do is set a default value (an empty array in your case) for your requests.
const [requests, setRequests] = useState([]);
May the force be with you.
I need to get a list of repositories using GitHub API, search has to work on button click and on change selectBox with licences
import React, { useState, useEffect, useCallback } from "react";
import axios from "axios";
import moment from "moment";
import { Layout } from "./../Layout";
import { List } from "./../List";
import { Loader } from "./../Loader";
import { Header } from "./../Header";
import { Search } from "./../Search";
import { Licenses } from "./../Licenses";
import "./App.css";
export const App = () => {
const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [hasError, setHasError] = useState(false);
const [nameSearch, setNameSearch] = useState("");
const [license, setLicense] = useState({});
const fetchData = useCallback(async () => {
setHasError(false);
setIsLoading(true);
try {
const prevMonth = moment()
.subtract(30, "days")
.format("YYYY-MM-DD");
const licenseKey = (license && license.key) || "";
const response = await axios(
`https://api.github.com/search/repositories?q=${nameSearch}+in:name+language:javascript+created:${prevMonth}${
licenseKey ? `+license:${licenseKey}` : ""
}&sort=stars&order=desc`
);
setData(response.data.items);
} catch (error) {
setHasError(true);
setData([]);
}
setIsLoading(false);
}, [license]);
useEffect(() => {
fetchData();
}, [fetchData]);
return (
<Layout>
<Header>
<Search
handleSearchChange={setNameSearch}
nameSearch={nameSearch}
isLoading={isLoading}
onSearch={fetchData}
/>
<Licenses license={license} handleLicenseChange={setLicense} />
</Header>
<main>
{hasError && <div>Error</div>}
{isLoading ? <Loader /> : <List data={data} />}
</main>
</Layout>
);
};
First of all, I get warning
Compiled with warnings.
./src/components/App/App.js
Line 42:6: React Hook useCallback has a missing dependency: 'nameSearch'. Either include it or remove the dependency array react-hooks/exhaustive-deps
And my search is not working because nameSearch is always empty in the query string.
How to make search work?
Try adding nameSearch to the list of dependencies for useCallback:
const fetchData = useCallback(async () => {
...
}, [license, nameSearch]);
and make sure setNameSearch is actually used inside Search.js so that it will have a value.
I am set up a Timeout for when the user stops typing for 3 seconds an api call is made and the ActivityIndicator appears.
edited with full code:
import React, { useState, useEffect } from 'react';
import { Text } from 'react-native';
import { ActivityIndicator } from 'react-native';
import {
Container,
SearchBar,
SearchBarInput,
SearchLoading,
SearchResultList,
Product,
} from './styles';
import api from '../../services/api';
export default function Search() {
const [searchResult, setSearchResult] = useState([]);
const [searchText, setSearchText] = useState('');
const [searching, setSearching] = useState(false);
const [focused, setFocused] = useState(false);
function renderProduct({ item }) {
return <Text>Oi</Text>;
}
let timer;
function handleChangeText(text) {
setSearching(false);
setSearchText(text);
clearTimeout(timer);
timer = setTimeout(() => setSearching(true), 3000);
}
useEffect(() => {
async function search() {
const response = await api.get(`products?search=${searchText}`);
setSearchResult(response.data);
setSearching(false);
}
if (searching) {
search();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searching]);
return (
<Container>
<SearchBar focused={focused}>
<SearchBarInput
placeholder="Pesquisar..."
onChangeText={handleChangeText}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
value={searchText}
/>
{searching && (
<SearchLoading>
<ActivityIndicator size="small" color="#000" />
</SearchLoading>
)}
</SearchBar>
<SearchResultList
data={searchResult}
keyExtractor={item => String(item.id)}
renderItem={renderProduct}
/>
</Container>
);
}
..............................................
But it's not working as it should:
https://user-images.githubusercontent.com/54718471/69919848-14680a00-1460-11ea-9047-250251e42223.gif
Remember that the body of the function is run on every single render. So the reference to the existing timer is lost each time the component re-renders.
You can use the useRef hook (https://reactjs.org/docs/hooks-reference.html#useref) to keep a stable reference across renders.
const timerRef = useRef(null);
function handleChangeText(text) {
setSearching(false);
setSearchText(text);
clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => setSearching(true), 3000);
}