I have this code in my app that will render a FlatList based in a information from a json file. I added a DropDown to be able to change the data, It's working but too late, I need to load the information before the app starts.
Code:
const data = require('../../utils/examData');
var index = 0;
var questions = data[index].Questions;
export function Home(): JSX.Element {
if(isLoaded == false){
useEffect(() => {
(async()=>{
await getData()
})();
}, [])
isLoaded = true;
}
async function getData() {
var result = await AsyncStorage.getItem('Config', (err, result) => {
if(result) {
var obj = JSON.parse(result);
index = obj.lastFile;
}
});
if(result) {
var obj = JSON.parse(result);
index = obj.lastFile;
}
questions = data[index].Questions;
};
let [currentCard, setCurrentCard] = useState(0);
cardListRef = useRef<FlatList>(null);
const [open, setOpen] = useState(false);
const [value, setValue] = useState(null);
const [items, setItems] = useState(data.map((examData, index) => ({ label: examData.FileName, value: index })));
async function changeExam(value) {
const config = {
lastFile: value
};
AsyncStorage.setItem(
'Config',
JSON.stringify(config)
);
Updates.reloadAsync();
}
return (
<View style={styles.container}>
<DropDownPicker
onSelectItem={(item) => {
changeExam(item.value);
}}
/>
</View>
);
}
I don't know how to make it load the getData() method first of all
Edit.: I tried to add it right after declaring the index variable at the very beginning of the code outside the Home() function, but then I get this error 'Invalid hook call. Hooks can only be called inside of the body of a function component.'
Related
I pull data from the API and write it to the application with usestate() when the app runs there are no problems, but after 10-30 seconds I get this error.
Here is my code.
const App = () => {
const [datas, setDatas] = useState([])
const res = async () => {
const response = await axios.get("http://hasanadiguzel.com.tr/api/kurgetir")
setDatas(response.data.TCMB_AnlikKurBilgileri)
}
res()
return (
<SafeAreaView style={style.container}>
<View>
{datas.map((item) => {
return (
<KurCard
title={item.Isim}
alis={item.BanknoteBuying}
satis={item.BanknoteSelling}
/>
)
})}
</View>
</SafeAreaView>
)
}
How can I fix this ?
Hi #n00b,
The problem is with your URL Protocol.
const App = () => {
const [datas, setDatas] = useState([]);
const res = async () => {
try {
const url = "https://hasanadiguzel.com.tr/api/kurgetir";
const response = await axios.get(url);
const data = await response.data;
console.log(data.TCMB_AnlikKurBilgileri); // check you console.
setDatas(response.data.TCMB_AnlikKurBilgileri);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
res();
}, []);
And also check this out:- Codesandbox.
And please read this Stack Overflow discussion for better understanding:- stackoverflow
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
I am trying to pass filtered value from JSON to the parent component, however I've tried using Set but seems the output is still the same. The component that I'm using to render the JSON is picker from native-base. I want to filter out the repeated value in my picker. Greatly appreciated if anyone can help me.
enter image description here
Here's my code.
Picker.js
const DefaultPicker = ({labelItem, pickerWidth, onHandleValue, ...rest}) => {
const context = useContext(WindowContext);
const [selectedValue, setSelectedValue] = useState('-Select-');
const [data, setData] = useState([]);
const {user, setUser} = useContext(AuthContext);
function onNewData() {
if (user) {
user.getIdToken().then((idToken) => {
Axios.get('URL_ENDPOINT', {
headers: {
Authorization: 'Bearer' + idToken,
},
})
.then(({data}) => {
setData(data.features);
// console.log(data.features);
})
.catch((error) => {
console.error(error);
});
});
}
}
useEffect(() => {
const form = onNewData(onNewData);
return form;
}, []);
return (
<PickerWrapper>
<PickerItem
width={pickerWidth}
height="60"
mode="dropdown"
selectedValue={selectedValue}
onValueChange={(itemValue, itemIndex) => {
setSelectedValue({itemValue});
}}>
{Array.from(
new Set(
data.map((value, index) => (
<PickerItem.Item
key={index}
label={value.properties[labelItem]}
value={value.properties[labelItem]}
{...rest}
/>
)),
),
)}
</PickerItem>
</PickerWrapper>
);
};
And here is my parent component
SiteData.js
const SiteData = () => {
const [values, setValues] = useState([]);
const onHandleValue = (params) => {
setValues(params);
console.log(params);
};
return (
<ScrollableView>
<DetailContainer>
<DetailWrapper>
<DetailTitle>Site Data</DetailTitle>
<DetailSubtitle marginTop="10">
Insert new data found during your audit or observation session
</DetailSubtitle>
<DetailSubcontainer>
<DefaultPicker
labelItem={'category'} <-- receive value from child
pickerWidth="100%"
onHandleValue={onHandleValue}
/>
</DetailSubcontainer>
</DetailWrapper>
</DetailContainer>
</ScrollableView>
);
};
UPDATE 1:
I'm using the filter() method so i can create a new array but it returns only one value in the picker list.
const indexData = data.filter(
({category}, index) => {
return (
data.findIndex(
(item) =>
item.category === category,
) === index
);
},
);
The output
enter image description here
I fixed my code by adding this on child component
var setObj = new Set();
var result = data.reduce((acc,item)=>{
if(!setObj.has(item.category)){
setObj.add(item.category,item)
acc.push(item)
}
return acc;
},[]);
I have the following React Native modules:
_localStorage.js
import AsyncStorage from '#react-native-community/async-storage';
const _storeData = async (key, value) => {
try {
await AsyncStorage.setItem(key, value);
} catch (error) {
console.log(error);
}
}
const _retrieveData = async (key) => {
try {
await AsyncStorage.getItem(key);
} catch (error) {
console.log(error);
}
}
export {_storeData, _retrieveData};
AppHeader.js
import React from 'react';
import {Button} from 'react-native-paper';
import {_retrieveData, _storeData} from '../../utils/_localStorage'
const LoginButton = () => {
return (
<Button icon='login' color='yellow' onPress={() => navigation.navigate('Login')}>
Login
</Button>
)
}
const UserButton = (user) => {
return (
<Button color='yellow' onPress={() => console.log('Botón usuario presionado...')}>
text
</Button>
)
}
const AppHeader = ({navigation, route}) => {
const user = _retrieveData('user');
console.log(user);
return user === ''? <LoginButton />: <UserButton user={user} />;
}
export default AppHeader;
I expect _retrieveData() to return the value of the key parameter, or null if it doesn't exits, but what I am getting in the console is this: {"_U": 0, "_V": 0, "_W": null, "_X": null}.
This is not how documentation of AsyncStorage indicates it works.
It's because you're not waiting for _retrieveData to finish. You're just setting user to the async function instead of waiting for its returned value.
Try something like this:
const AppHeader = ({navigation, route}) => {
const [user, setUser] = useState();
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
fetchUser();
}, [])
const fetchUser = async () => {
setIsLoading(true);
const userData = await _retrieveData('user');
setUser(userData);
setIsLoading(false);
}
if (isLoading) return <LoadingIndicator />
if (!!user) return <UserButton user={user} />
return <LoginButton />;
}
I've called fetchUser in the initial useEffect that gets called when the AppHeader component is first loaded. It sets a loading boolean to true and then requests the user data. When the userData is returned it sets it in state and sets loading to false.
You don't need the loading bit but I included it otherwise your app would show the login button while it's fetching the data. You'll have to create the LoadingIndicator component yourself.
_retrieveData is returning promise here. You need to await for that promise to resolve. Try writing it like this:
const _retrieveData = async (key) => {
try {
const data = await AsyncStorage.getItem(key);
return data;
} catch (error) {
console.log(error);
}
}
AppHeader.js
const AppHeader = ({navigation, route}) => {
_retrieveData('user').then((user)=>{
console.log(user);
return user === ''? <LoginButton />: <UserButton user={user} />;
});
}
Read this for more clarity : https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await
You're not returning anything from your _retrieveData function. Try writing it like so:
const _retrieveData = async (key) => {
try {
const data = await AsyncStorage.getItem(key);
return data;
} catch (error) {
console.log(error);
}
}
I am trying to make a membership login system but I get an error. I couldn't understand much because I had just started. What's the problem?
export default async () => {
const [isLoading, setIsLoading] = React.useState(true);
const [userToken, setUserToken] = React.useState(null);
const AsyncUserValue = await AsyncStorage.getItem('userid');
console.log(AsyncUserValue); // (userid 15)
if(AsyncUserValue != null){
console.log('AsyncStorageParse: ' + AsyncUserValue); // (userid 15)
setUserToken(AsyncUserValue);
console.log('Tokken: ' + userToken); // NULL
}
React.useEffect(() => {
setTimeout(() =>{
setIsLoading(false);
}, 1000);
}, []);
if(isLoading) { return <SplashScreen /> }
return(
<NavigationContainer>
{userToken ? (
<AppTabs />
) : (
<LoginStack />
) }
</NavigationContainer>
)
}
You returned functional component as asynchronous (note top export default async ()).
You can't do that - components are required to return React elements (so they have to be synchronous).
What you can do instead is to create a inner async function and do all your async logic there:
export default () => {
const [state, updateState] = useState();
async function myWork() {
const data = await getDataAsyncWay();
updateState(data);
}
useEffect(() => {
myWork()
}, [])
return <View>{...}</View>
}
Note: avoid exporting anonymous function as components - this way, their name won't be visible in stack trace (see your screenshot). What you can do instead is:
function MyComponent() {...};
export default MyComponent