Unable to use Dynamic data into react-native-chart-kit - react-native

I tried to render fetched data from the firestore and display them into react-native-chart-kit. However, I always face the below error:
invalidNumber: M0,0 L-Infinity,181 L64,181
I got data correctly from the database by this function:
const getweight = () =>
firebase
.firestore()
.collection("weights")
.orderBy("time")
.where("user", "==", "1")
.get()
.then((querySnapshot) => {
let result = [0];
querySnapshot.forEach((doc) => {
result.push(+doc.data().Weight);
});
setDateTracker(date);
if (result.length) {
setWeighTracker(result);
}
});
so, I got the array with the right data.
Then, I push data to the state below:
const [weightTracker, setWeighTracker] = useState([0]);
when I try to display data to the chart like below, I got error of invalid number
<LineChart
data={{
datasets: [
{
data: weightTracker.map((item) => {
return item.Weight;
}),
},
],
}}
width={Dimensions.get("window").width} // from react-native
height={220}
yAxisLabel=" KG "
//withVerticalLabels={false}
chartConfig={{
backgroundColor: "#e26a00",
backgroundGradientFrom: "#fb8c00",
backgroundGradientTo: "#ffa726",
decimalPlaces: 0, // optional, defaults to 2dp
color: (opacity = 1) => `rgba(255, 255, 255, ${opacity})`,
style: {
borderRadius: 1,
},
}}
bezier
style={{
marginVertical: 8,
borderRadius: 16,
}}
/>

I encountered the same error even when the data is with initial state of 0 . What fixed mine is to only display the chart once data is loaded.
You may add an if check like this before your chart code:
if (weightTracker?.length === 0) {
return (
<View style={styles.screen}>
<Text>No chart data to display!</Text>
</View>
);
}
return (...<your linechart code>)
For the if check to work, you also need to set the initial state of weightTracker to just an empty array.
Hope this helps.

Related

INVALID_STATE_ERR (React native)

What i am trying to do is simple. But not having enough knowledge about react state caused the problem. In my tab i took typing useState as false. I am trying to get the state when user is typing. By connecting websocket when user is typing i am sending type: "is_typing" data. And when receive is_typing i am trying to change the typing state to true and set timeout 500ms so that it won't show typing if it doesn't get onkeypress.
const Sock = () => {
const [typing, setTyping] = useState(false)
const url = endpoint + "chat/" + "thorOdin" + "/" + "?token=" + token
const ws = new WebSocket(url)
useEffect(() => {
if (token) {
ws.onopen = function () {
console.log("Chat Websoket Connected");
};
ws.onmessage = function (event) {
const data = JSON.parse(event.data);
if (data.command === "is_typing") {
setTyping(true)
setTimeout(() => {
setTyping(false)
}, 500);
}
};
ws.onerror = (e) => {
// an error occurred
console.log(e.message);
};
ws.onclose = function () {
console.log("WebSocket Client disconnected");
};
}
}, []);
function typingHandle(e){
ws.send(
JSON.stringify({
command: "is_typing",
text: `is typing ...`,
user: "me",
})
);
}
return (
<View>
{typing==true?
<Text style={{marginTop:50}}>Typing</Text>:
<View></View>
}
<TextInput placeholderTextColor={'gray'} style={{
color: 'white',
backgroundColor: '#262626',
borderRadius: 20,
paddingLeft: 10,
height: 40,
width: '80%',
marginLeft: 10,
marginTop:100
}} placeholder='Write Message..'
onKeyPress={typingHandle}/>
</View>
)
}
It's working. But after 4-5 sec it showing Uncaught error "INVALID_STATE_ERR". Can anyone tell what am i doing wrong here???
I am trying to get typing state of user through socket connection.

Can't test Apollo GraphQL query's loading state

guys. I have a query that gets data from backend, while query didn't get data I'm showing '...Loading' text. Now I want to test it, but I can't. Test's logic is: if loading state is true, check if we have '...Loading' text.
Here is my query:
const [getCards, { data: cardsData, error: cardsError, loading: cardsLoading }] = useLazyQuery(
GET_LIST,
{
fetchPolicy: 'no-cache',
}
);
Here is my check of loading state:
if (cardsLoading) {
return (
<View
style={{
width: '100%',
height: '100%',
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>...Loading</Text>
</View>
);
}
And finally here is my test:
const getCardsMock = [
{
request: {
query: GET_LIST,
},
result: {
cards: {
id: 0,
name: 'card',
},
}
},
];
it('Loading state test', async () => {
const component = renderer.create(
<MockedProvider mocks={getCardsMock} addTypename={false}>
<HomeViewApollo />
</MockedProvider>
);
const tree = component.toJSON();
expect(tree.children).toContain('...Loading');
});
After running test I got an error telling me that expected value isn't equal to received value. Received value is array with component, that I render, if loading is done.
I'm not sure but looks like that component's loading state never changes during the test. Do you have an idea how to fix it?
I rewrote test like that
it('Loading state test', async () => {
let wrapper = render(
<MockedProvider mocks={[getCardsMock]} addTypename={false}>
<HomeViewApollo />
</MockedProvider>
);
await waitFor(() => [
expect(wrapper.queryByTestId('loadingIndicator')).toBeTruthy()
]);
});
Now it works.

How to use decorator in react-native-chart-kit?

I'm discovering react-native-chart-kit and I found that in LineChart, we can use a function named: decorator but I didn't found any example ;
The link of documentation
If someOne has already experience on this can help me
componentDidMount() {
this.GetData();
}
GetData = () => {
const self = this;
return fetch('http://192.168.1.3:80/graph.php')
.then((response) => response.json())
.then((responseJson) => {
const dataClone = {...self.state.data}
const values = responseJson.map(value => value.ChiffreAffaire);
const label = responseJson.map(value => value.M500_NOM);
dataClone.datasets[0].data = values;
dataClone.labels= label;
this.setState({
isLoading: false,
data: dataClone,
});
})
.catch((error) =>{
console.error(error);
});
}
<LineChart
data={this.state.data}
width={Dimensions.get("window").width*0.99}
height={400}
yAxisInterval={1}
chartConfig={chartConfig}
bezier
spacing={0.8}
spacingInner={0.8}
verticalLabelRotation={90}
withInnerLines={true}
//renderDotContent={({x, y, index}) => <Text>{}</Text>}
style={{
marginVertical: 20,
marginLeft:2,
marginRight:2,
borderRadius: 16,
borderWidth: 0.5,
borderColor: 'grey'
}}
/>
And this is what shown in my mobile screen (So as you see my x labels are not presenting correctely ) Any Idea??????
I found this nice tutorial:
https://levelup.gitconnected.com/adding-tooltip-to-react-native-charts-67606c5d3182
You should basically get the point data (setting a callback to onDataPointClick property from LineChart component) and transfer the point coordinates and value to the tooltip, a SVG component.

How to save React Native phone sensor data to an array without using state arrays (getting Maximum update depth exceeded)?

I'm having some trouble figuring out how to handle sensor data in React Native. My goal is to get phone sensor data for a limited time (let's say 10 seconds) and after completion save the list obtained to local database. However pushing new gyroscope value to state array is one option but getting maximum update depth exceeded error in there. The code is below.
import React from 'react';
import {
StyleSheet,
View,
Text,
Dimensions,
} from 'react-native';
import { Gyroscope } from 'expo-sensors';
import { Button } from "native-base";
import CountDown from 'react-native-countdown-component';
import UUID from 'pure-uuid';
import {insertGyroData} from './measureService';
const SAMPLING_RATE = 20; // defaults to 20ms
export class Measure extends React.Component {
state = {
gyroscopeData: {},
measuringStarted: false,
x_gyro: [0],
y_gyro: [0],
z_gyro: [0]
};
componentDidMount() {
this.toggleGyroscope();
}
componentWillUnmount() {
this.unsubscribeAccelerometer();
}
toggleGyroscope = () => {
if (this.gyroscopeSubscription) {
this.unsubscribeAccelerometer();
} else {
this.gyroscopeSubscribe();
}
};
gyroscopeSubscribe = () => {
Gyroscope.setUpdateInterval(20);
this.gyroscopeSubscription = Gyroscope.addListener(gyroscopeData => {
this.setState({ gyroscopeData });
});
};
unsubscribeAccelerometer = () => {
this.gyroscopeSubscription && this.gyroscopeSubscription.remove();
this.gyroscopeSubscription = null;
};
referenceMeasurementCompleted = async (x, y, z) => {
this.setState({ measuringStarted: false });
const uuid = new UUID(4).format();
await insertGyroData([uuid, 'admin', '12345', x.toString(), y.toString(), z.toString()]);
alert('Reference measurements completed');
};
render() {
let { x, y, z } = this.state.gyroscopeData;
const { x_gyro, y_gyro, z_gyro } = this.state;
if (this.state.measuringStarted) {
this.setState(({ x_gyro: [...x_gyro, x], y_gyro: [...y_gyro, y], z_gyro: [...z_gyro, z] }));
return (
<View style={styles.container}>
<Text>Gyroscope:</Text>
<Text>
x: {round(x)} y: {round(y)} z: {round(z)}
</Text>
<Text>Time spent:</Text>
<CountDown
until={5}
size={30}
onFinish={() => this.referenceMeasurementCompleted(x_gyro, y_gyro, z_gyro)}
digitStyle={{ backgroundColor: '#FFF' }}
digitTxtStyle={{ color: '#00c9ff' }}
timeToShow={['M', 'S']}
timeLabels={{ m: 'MM', s: 'SS' }}
/>
</View>
);
} else {
return (
<View style={styles.container}>
<Button
rounded
primary
onPress={() => this.setState({ measuringStarted: true })}
>
<Text style={styles.buttonText}>Start reference measurements</Text>
</Button>
</View>
);
}
}
}
function round(n) {
if (!n) {
return 0;
}
return Math.floor(n * 100) / 100;
}
const styles = StyleSheet.create({
container: {
flex: 1,
height: Dimensions.get('window').height,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center'
},
buttonText: {
color: 'white',
fontSize: 20,
paddingHorizontal: 10,
},
});
With the current solution I'm getting Maximum Depth Update Exceeded error
Please help!
The error is obviously coming from this block since you are changing state inside the render.
if (this.state.measuringStarted) {
this.setState(({ x_gyro: [...x_gyro, x], y_gyro: [...y_gyro, y], z_gyro: [...z_gyro, z] }));
}
One way you can try is updating the state in
componentDidUpdate(prevProps,prevState){}
But take care that it does not lead to Maximum Stack Reached error. With proper conditional statements you can set the state to new data without infinite recursion.

How to change a string in React-Native into a Component?

I have a var str = '<Text> something </Text>', can I render it into a Component?
I tried the following, but it doesn't work :(
var str = '<Text>test</Text>'
render(){
return(
<Text>{str}</Text>
)
}
Is there any way to do this, similar to dangerouslySetInnerHTML in React?
In my project, I get json by fetch just like
{
content:'<p>example</p>'
}
I want to replace the html element p or others into Textand so on by regular expressions, but the result is a string.
I also tried the react-native-html-render but it's document is incomprehensible and doesn't performs well.
You could create a function that converts the inner html of the response you get into a RN Text element.
var str = '<p>something here</p>'
var convertToText = (response) => {
var text = response.split(/[^A-Za-z]/).filter(x => x !== '').slice(1, -1).join(' ') //gives "something here";
return <Text>text</Text>;
}
convertToText(str) === <Text>something here</Text>
Set it as part of your state (e.g., this.state.str). In your constructor, give it a default value (e.g., this.state = {str: "test"}). Then in your function that does the fetch, do setState to change the value (e.g., this.setState({str: response})). Finally, in your render, do this:
render() {
return (
<View style={styles.container}>
<Text>{this.state.str}</Text>
</View>
);
}
var str = <Text>test</Text>;
render() {
return (
<View style={styles.container}>
{str}
</View>
);
}
Unfortunately the tag needs to be incorporated at compile-time, as JSX becomes transpiled into React.createElement calls. Or you can write the React.createElement calls yourself.
For example, you can create a parser than can walk the JSX tree in your server response. Something like
function parseResponseIntoJSXTree (string) {
// very psuedo example:
var type = parseJSXTag(string) // produces `Text`
var props = parseJSXTagProps(string) // produce any attribute=values
var innerContent = parseJSXContent(string) // produces 'something'
return React.createElement(type, props, children)
}
This only scratches the surface, as you'd need to walk the tree if there are child elements deeper than the root node.
Want my horrible, horrible answer?
Include babel in your bundle and do:
var renderableContent = require('babel-core', {presets: ['react-native'])
.transform('<Text>something</Text>')
Note - I highly discourage this, but it technically would work, unless babel requires on node dependencies that won't exist in the runtime (likely).
My requirement is similar, to make dynamic screens with arbitrary components, depending on JSON server response. Here is what I did:
const blockContent = [
{
type: 'text',
content: 'Some title',
size: 20,
color: 'black',
wrapperPadding: 10
},
{
type: 'text',
wrapperPadding: 10,
size: 16,
color: 'red',
content: 'Some text. Some text. Some text. Some text. Some text. '
},
{
type: 'text',
wrapperPadding: 10,
size: 16,
color: 'red',
content: 'Some text. Some text. Some text. Some text. Some text. '
},
{
type: 'link',
content: 'Some link',
size: 16,
color: 'blue',
wrapperPadding: 10,
url: 'http://www.google.com'
}
];
class CustomBlock extends Component {
openURL (url) {
Linking.openURL(url).catch(err => console.error('An error occurred', err));
}
render () {
return (
<View style={styles.container}>
{blockContent.map((item) => {
switch (item.type) {
case 'text':
return (
<View style={{padding: item.wrapperPadding}}>
<Text style={{fontSize: item.size, color: item.color}}>{item.content}</Text>
</View>
);
case 'link':
return (
<TouchableHighlight style={{padding: item.wrapperPadding}} underlayColor="lightgrey" onPress={this.openURL.bind(this, item.url)}>
<Text style={{fontSize: item.size, color: item.color}}>{item.content}</Text>
</TouchableHighlight>
);
}
})}
</View>
);
It's quite easy to declare all components you expect to use like I did with text and link, and to style them as well.