I have here my code that gets me the btc price
function Nft() {
const[price,setPrice]=useState(0);
setTimeout(() => {
fetch('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd')
.then((res)=>res.json())
.then((response)=>{
//your response is like this ,{"bitcoin":{"usd":18993.39}}
let price= response?.bitcoin?.usd;
setPrice(price)
})
.catch((error)=>{
//here you can manage errors
})
}, 12000);
return (
<View>
<Text style={style.header}>Bitcoin Price</Text>
<Text >{price}</Text>
</View>
);
};
export default Nft
function Nft() {
const[price,setPrice]=useState(0);
setTimeout(() => {
fetch('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd')
.then((res)=>res.json())
.then((response)=>{
//your response is like this ,{"bitcoin":{"usd":18993.39}}
let price= response?.bitcoin?.usd;
setPrice(price)
})
.catch((error)=>{
//here you can manage errors
})
}, 12000);
return (
<View>
<Text style={style.header}>Bitcoin Price</Text>
<Text >{price}</Text>
</View>
);
};
export default Nft
I want to update the price every 2 minutes on my UI, if I use it like it is right now, when the app loads it displays 0 for the first 2 mintes than the price gets updated, but it wont get updated every 2 minutes
HOw can i fix this?
Replace the setTimeout with setInterval.
setTimeout runs only once after the given timeout.
Related
I'm trying to open a simple page with React Native WebView.
It's a single page web, and when you do a search, it prints out some information about your search.
After that, if you want to search again, press the back button on the device to move to the search box.
Because it is a single page, I cannot use goBack, so I created a function called cancel.
The problem is that when I click the device's back button, the function called cancel defined on the web is not executed.
The cancel function deletes the searched information and returns to the search window.
I will upload my code.
Please advise.
export default function App() {
const webviewRef = useRef(null);
const backAction = () => {
setBackTapping((prev) => prev += 1);
webviewRef.current.injectJavaScript('window.cancel()')
return true;
}
useEffect(() => {
const timer = setInterval(() => {
setBackTapping(0)
}, 1000)
return () => clearInterval(timer);
}, [])
useEffect(() => {
const backHandler = BackHandler.addEventListener('hardwareBackPress',backAction);
return () => backHandler.remove()
}, [])
useEffect(() => {
if(backTapping >= 2){
return BackHandler.exitApp();
}
},[backTapping])
return (
<KeyboardAvoidingView style={{ flex: 1 }}>
<StatusBar hidden />
<WebView
ref={webviewRef}
textZoom={100}
originWhitelist={['*']}
javaScriptEnabled
source={{ uri: 'myhome.com'}}
startInLoadingState={true}
/>
</KeyboardAvoidingView>
);
}
Expected behavior:
The cancel function is executed, all open windows are closed, and you are returned to the search window.
in my case, calling is wrong.
instead of :
webviewRef.current.injectJavaScript('window.cancel()')
use :
const generateOnMessageFunction = (data) => `
(function(){
window.dispatchEvent(new MessageEvent('message',{data: ${JSON.stringify(data)}}));
})()
`;
webviewRef.current.injectJavaScript(generateOnMessageFunction('cancel'));
detail referance :
https://github.com/react-native-webview/react-native-webview/issues/809
I have a Component where i'm maping an Array to render data. But before that, i need to call API taking id from each objects of the array to modify the array. Now, i am calling the API's in a function and calling the function in useEffect() . But when i do that, it continues to an infinite loop. Here's how my component looks like:
const DemoComponent = (props) => {
const [renderArr, setRenderArr] = useState([]);
useEffect(() => {
getStatus();
},[renderArr])
const getStatus= async() =>{
var arr = [
{id: 1,name: Leather},
{id: 2,name: Shoe},
{id: 3,name: Belt},
]
var firstStatus = await API(arr[0].id , props.token)
var secondStatus = await API(arr[1].id , props.token)
var thirdStatus = await API(arr[2].id , props.token)
var statusObj = [
{ status: firstStatus.status },
{ status: secondStatus.status },
{ status: thirdStatus.status },
]
var mergedArray = newArr.map((e, i) => ({ ...e, ...statusObj[i] }));
setRenderArr(mergedArray);
}
}
return (
<View style={styles.container}>
{mergedArray.map((item, index) => {
return (
<TouchableOpacity>
<Text style={{ color: '#FFF' }}>{item.status}</Text>
</TouchableOpacity>
);
})}
</View>
);
};
Now, how can i stop this infinite loop. But in the meantime, i want to rerender when renderArr changes props.
Because you trigger the renderArr. You should do one call only when the component is mounted such as below snippet.
useEffect(() => {
getStatus();
}, []);
There are a few items I would like to point out here,
You have side effect registered for renderArr, which calls a function that updates renderArr. Any change in renderArr will invoke the side effect and this loop goes on forever.
Inside the getStatus function, you are updating your renderArr state after your application logic. But the render part is referring to some other variable mergedArray. You will have to change it to renderArr state.
const DemoComponent = (props) => {
const [renderArr, setRenderArr] = useState([]);
useEffect(() => {
getStatus();
},[]); // removed dependency to call it only once, (on mount)
const getStatus= async() =>{
....
setRenderArr(mergedArray); // state is updated with the new processed value
}
return (
<View style={styles.container}>
{renderArr.map((item, index) => { // changed to the state variable
return (
<TouchableOpacity>
<Text style={{ color: '#FFF' }}>{item.status}</Text>
</TouchableOpacity>
);
})}
</View>
);
};
My entered text in SearchBar is getting cleared automatically after some milliseconds when I start typing into it. It goes back to the placeholder state, what could be the issue?
Here is my code:
return (
<View style={{ marginTop: 30 }}>
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={this.handleSearch}
/>
</View>
);
};
this is the code for handleSearch method,
handleSearch = (text) => {
const formatQuery = text.toLowerCase();
const data = _.filter(this.state.fullData, (user) => {
return contains(user, formatQuery);
});
this.setState({ query: formatQuery, data }, () => this.makeRemoteRequest());
};
code for makeRemoteRequest():
makeRemoteRequest = _.debounce(() => {
this.setState({ loading: true });
getUsers(20, this.state.query)
.then((users) => {
this.setState({
loading: false,
data: users,
fullData: users,
});
})
.catch((error) => {
this.setState({ error, loading: false });
});
}, 250);
The issue persists even if I remove the debounce method so I think the issue is related to something else.
Also, this is like my 3rd day with react-native development so please excuse any newbie mistakes.
if i am not mistaken, you should add a value prop to the SearchBar, and put a state variable like so :
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={this.handleSearch}
value={this.state.text} //use deconstructing
/>
And update it before doing your handle search
handleSearch = (text) => {
this.setState({text})
const formatQuery = text.toLowerCase();
const data = _.filter(this.state.fullData, (user) => {
return contains(user, formatQuery);
});
this.setState({ query: formatQuery, data }, () => this.makeRemoteRequest());
};
You should check the SearchBar doc from react-native-elements : https://react-native-elements.github.io/react-native-elements/docs/searchbar.html
So what I'm trying to do is fetching data from an API (works well), that has this autocomplete function.
Link to example: https://autocomplete.aws.dk/
Link to the guide: https://autocomplete.aws.dk/guide2.html
What is hard for me, is that the guide is HTML, and this is suppose to work in React Native.
So far I made an input field, that can detect when writing minimum two letters will show a list of addresses.
What I want is when the address is clicked, it takes that value and places it in the input field.
Heres my code:
The API fetch:
import React from "react";
import url from "./url";
export default class DawaFetch extends React.Component {
static defaultProps = {
options: {},
minCharacters: 2,
};
state = {
value: "",
suggestions: [],
};
handleChange = ({ target: { value } }) => {
this.setState({ value });
if (this.props.minCharacters <= value.length) this._fetch(value);
};
_fetch = (value) => {
fetch(
url("https://dawa.aws.dk/adresser/autocomplete", {
q: value,
["per_side"]: 100,
...this.props.options,
}),
{
method: "GET",
headers: {
"Accept-Encoding": "gzip, deflate",
},
}
)
.then((response) => response.json())
.then((json) => this.setState({ suggestions: json }))
.catch((err) => console.error("parsing failed", err));
};
render = () =>
this.props.children({ ...this.state, handleChange: this.handleChange });
}
And here is my view:
<DawaFetch>
{({ value, suggestions, handleChange }) => {
console.log(suggestions);
return (
<View>
<CustomInputs
type="text"
value={value}
onChange={handleChange}
/>
{suggestions.map((suggestion) => (
<TouchableOpacity>
<NormalText key={suggestion.adresse.id}>{suggestion.tekst}</NormalText>
</TouchableOpacity>
))}
</View>
);
}}
</DawaFetch>
Tried different solutions by making it a FlatList with renderItem, and making an onPress function, but I could never make it work.
Hopefully someone can guide me in the right direction, I might been overthinking this.
React-Native TextInput onChange value is not available inside the target as it's available in HTML, Remove target from handleChange function like below, also it's not onChange it's onChangeText in TextInput.
handleChange = (value) => {
this.setState({ value });
if (this.props.minCharacters <= value.length) this._fetch(value);
};
You can achieve your desired functionality in a very simple manner.
Add this to your DawaFetch class.
OnItemSelection=(address)=>{
this.setState({value: address})
}
Add this to your render Function
render = () =>
this.props.children({ ...this.state, handleChange: this.handleChange, OnItemSelection: this.OnItemSelection });
}
Then make these changes in your DawaFetch component
<DawaFetch>
{({ value, suggestions, handleChange, OnItemSelection }) => {
console.log(suggestions);
return (
<View>
<CustomInputs
type="text"
value={value}
onChangeText={handleChange}
/>
{suggestions.map((suggestion) => (
<TouchableOpacity onPress={()=> OnItemSelection(suggestion.adresse)}>
<NormalText key={suggestion.adresse.id}>{suggestion.tekst}</NormalText>
</TouchableOpacity>
))}
</View>
);
}}
</DawaFetch>
Edit:Here is Snack having solution
https://snack.expo.io/#waheed25/bad-raisins
i try fetch info from server and while fetching not complete show searching Component and when fetch done show information...everything is OK...But when NET offline show searching component and when turning on NET i want show a button "Try Again" and when click on "FetchData" function run again.
constructor(){
super();
this.state={
isLoading:true,
dataSource:null,
dataError:false
}
}
componentDidMount(){
FetchData = () => {
return fetch(SuperUrl+'/info')
.then((response)=>response.json())
.then((responseJson)=>{
this.setState({
isLoading:false,
dataSource: responseJson
})
})
.catch((error)=>{this.setState({dataError:true})})
}
FetchData()
}
render() {
if(this.state.dataError)
{
<View>
<Buttom onpress={()=>{FetchData()}}>
<Text>Try Again</Text>
<Button>
</View>
}
if(this.state.isLoading)
{
return(
<Container>
<StatusBar backgroundColor={'#3949ab'}/>
<Searching/>
<JaFooter list={{backgroundColor:'#3949ab', color:'#ffffff'}}/>
</Container>
)
}
else
{
let regionName = this.state.dataSource.map((value,key)=>{
return(
<ListItem key={key} >
<Text key={key} style={styles.cityName}>{value.name}</Text>
</ListItem>
)
})
Your title is very misleading as componentDidMount should run once. What you want is totally different so I'll explain. Since you are asking for something that goes against the pattern of RN you are risking getting your answer closed. Anyway..
Fetch does not support a timeout natively. But you can workaround it by running two promises. One for your fetch, and another for the timeout. This is pseudo code, you are gonna have to learn about Promise.race and how setTimeout works.
const promise1 = fetch;
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject(new TimeoutError()), 30000);
})
Promise.race([promise1, promise2])
.then(() => {
//You got a response from the server
})
.catch(err => {
//Probably timeout
if (err.code == 'Timeout')
this.setState({loading: false, showTryAgain: true})
})
Promise.race will run both in parallel but if the network request for the fetch ends first, it will ignore the timeout which is what you want.