I am trying to build a water reminder app. I have 3 screens and I am using react-navigation
Home (that I allow users to increase their amount drink that day and display how much water they drunk)
Notifications (where users defining with switch buttons if they want
to receive notifications and when to receive)
Settings (where the user enters age, weight to determine how much they
should drink daily). this is the first screen users see when they
downloaded the app
I am setting the drunk value to zero after every day with a check date function. My problem is drunk value is not automatically set to zero after loading the app. Rest of the values such as progress are set to zero but not drunk value. When I change one screen and come back to the Home screen, it is set to zero.
state = {
progress: 0,
drunk: 0,
open: false,
goal: 0,
};
componentDidMount() {
this.willFocusSubscription = this.props.navigation.addListener('willFocus', payload => {
// perform check when the component mounts
this.checkDate();
// retrieve data from AsyncStorage
this._retrieveData();
});
}
// function to retreive data from AsyncStorage
_retrieveData = async () => {
try {
const sliderValue = await AsyncStorage.getItem('sliderValue');
const drunk = await AsyncStorage.getItem('drunk');
const progress = await AsyncStorage.getItem('progress');
if (sliderValue !== null) {
// We have data!! ve stateleri belirledik
this.setState({ goal: parseInt(sliderValue) });
} else if (sliderValue === null) {
this.setState({ goal: 0 });
}
if (drunk !== null) {
this.setState({ drunk: parseInt(drunk) });
} else if (drunk === null) {
this.setState({ drunk: 0 });
}
if (progress !== null) {
this.setState({ progress: parseFloat(progress) });
} else if (progress === null) {
this.setState({ progress: 0 });
}
} catch (error) {
console.log(error.message);
}
};
// function to check date and set drunk to zero
checkDate = async () => {
// create a string with the current date
let currentDateString = moment().format('DDMMYYYY');
// get the value from storage
let savedDateString = await AsyncStorage.getItem('storedDate');
// create variables for differences on year month
let yearDiff = currentDateString.slice(4) - savedDateString.slice(4)
let monthDiff = currentDateString.slice(2, 4) - savedDateString.slice(2, 4)
let dayDiff = currentDateString.slice(0, 2) - savedDateString.slice(0, 2)
// if there is a value on AsyncStorage
if (savedDateString) {
// if difference is bigger than zero set drunk and progress to zero
if (yearDiff > 0 || monthDiff > 0 || dayDiff > 0) {
// this is where you put the code that resets everything
// clear the values that you have previously saved
// remember to save the new date
this.setState({ drunk: 0, progress: 0 }, () => {
this._storeData();
});
try {
await AsyncStorage.setItem('storedDate', currentDateString);
} catch (err) {
console.debug(err.message);
}
}
} else {
// save the time as this is the first time the app has launched
// do any other initial setup here
try {
await AsyncStorage.setItem('storedDate', currentDateString);
} catch (err) {
console.debug(err.message);
}
}
};
render() {
return (
<Provider>
<View style={styles.container}>
<Text style={styles.drunk}>
{this.state.drunk.toFixed(0)} / <Text style={styles.goal}>{this.state.goal}</Text>
</Text>
</View>
</Provider>
)
}
Problem I was encountering that I am calling AsyncStorage method inside navigation listener. Since AsyncStorage is working asynchronously,AsyncStorage methods are not completed inside navigation listener.
How I did solve this issue is that I made componentDidMount async function and call methods outside of navigation listener with await.
async componentDidMount() {
// perform check when the component mounts
await this.checkDate();
// retrieve data from AsyncStorage
await this._retrieveData();
this.willFocusSubscription = this.props.navigation.addListener('willFocus', payload => {
// perform check when the component mounts
this.checkDate();
// retrieve data from AsyncStorage
this._retrieveData();
});
}
Related
I created a function that fetches cart items from the API
There are 2 API one is for authenticated users and the other is for non-authenticated users
But the execution of the non-authenticated users takes more time to get data that's why it even overwrites the data for the authenticated user
So how can I solve this problem
Inside the context
usestate to call the cart function
useEffect(() => {
console.log("the value of user", userAuthenticated);
if (userAuthenticated == true) {
cartdataupdater("useEffect");
} else {
cartdataupdater("useEffect withou");
}
}, [userAuthenticated]);
cart updater
const cartdataupdater = (from = "this") => {
console.log(" worken", userAuthenticated, from);
var startTime = performance.now();
if (userAuthenticated == true) {
axios.get(`http://127.0.0.1:8000/core/cart/1/`).then((response) => {
setcartData(response.data);
console.log("the end is not good");
var endTime = performance.now();
console.log(
`Call to doSomething took ${
endTime - startTime
} milliseconds`
);
});
} else {
console.log("not authenticated");
var startTime = performance.now();
axios
.get(
`http://127.0.0.1:8000/core/dcart/${localStorage.getItem(
"cart_id"
)}`
)
.then((response) => {
setcartData(response.data);
console.log("the end");
console.log(from);
var endTime = performance.now();
console.log(
`Call to doSomething took ${
endTime - startTime
} milliseconds`
);
});
}
};
Thanks in advance
I anything is required I will provide you
I founded the soltion
I solve it by adding the loading state which helps me to stop the extra request
const cartdataupdater = () => {
if (userAuthenticated == true) {
axios.get(`http://127.0.0.1:8000/core/cart/1/`).then((response) => {
setcartData(response.data);
});
} else {
try {
axios
.get(
`http://127.0.0.1:8000/core/dcart/${localStorage.getItem(
"cart_id"
)}`
)
.then((response) => {
setcartData(response.data);
});
} catch (error) {
console.log(error);
}
}
};
useEffect(() => {
if (userauthloading == false) {
cartdataupdater();
}
}, [userauthloading, userAuthenticated]);
I'm fetching weather data from OpenWeather API for a given location and want to check, every minute, whether that data is still current (and if not, change it). I've used setInterval but the data doesn't seem to update every minute--here are the functions in question.
In the controller...
const controlStation = async function (station) {
try {
// Updates weather given station
const weather = await model.updateWeather(station);
// Periodically checks if weather data is current
// If not, updates weather
let checkWeather = await model.checkWeather(station);
setInterval(checkWeather, 1 * MINUTES);
// Renders weather
weatherView.render(model.stations[station], weather);
} catch (err) {
console.log(err);
}
};
controlStation("fbi");
In the model...
export const state = {};
export const stations = {
fbi: {
name: "fbi",
city: "Sydney, Australia",
coordinates: [-33.5346, 151.12],
},
kutx: {
name: "kutx",
city: "Austin, Texas, United States of America",
coordinates: [30.1721, -97.4402],
},
cism: {
name: "cism",
city: "Montreal, Quebec, Canada",
coordinates: [45.3023, -73.3644],
},
};
export const updateWeather = async function (station) {
try {
const [lat, lng] = stations[station].coordinates;
const url = `${API_WEATHER_URL}lat=${lat}&lon=${lng}&appid=${API_WEATHER_KEY}&units=imperial`;
const data = await fetch(url);
const weather = await data.json();
state.station = station;
state.weather = weather;
return weather;
} catch (err) {
console.error(err);
}
};
export const checkWeather = async function (station) {
try {
console.log("Checking weather!");
const needsUpdate = false;
const prev = state;
console.log("prev", prev.weather);
const cur = await updateWeather(state.station);
console.log("cur", cur);
if (
prev.weather.wind.speed !== cur.wind.speed ||
prev.weather.wind.dir !== cur.wind.dir ||
prev.weather.main.temp !== cur.main.temp ||
prev.weather.weather[0].description !== cur.weather[0].description
) {
console.log("Changing now");
needsUpdate = true;
} else console.log(`They were same at ${Date.now()}`);
return needsUpdate;
} catch (err) {
console.error(err);
}
};
I know I still need to do something if the weather data has changed and is different than what's in state, but I don't even see it making a new comparison through the checkWeather function every minute.
/////////////////
UPDATE--
I discovered that the issue was that async functions are incompatible with vanilla JS setInterval. There's a node package for creating setInterval with an async callback function but I don't know Node yet so instead I grabbed this workaround off another StackOverflow answer.
async function execute1() {
while (true) {
await new Promise((resolve) => setTimeout(resolve, 2 * MINUTES));
await model.checkWeather(station);
}
}
execute1();
Now my program is successfully checking the results from a new API call to the data stored in state. It recognizes when things have changed and when they haven't and now I'm going to update state when the weather conditions have changed. Thanks all for the help!
I want to add a pagination to my app for this reason i coded below code but there is a problem.
Here is my useEffect:
useEffect(() => {
let x = null;
const unsubscribe = chatsRef
.orderBy("createdAt", "desc")
.limit(10)
.onSnapshot((querySnapshot) => {
const messagesFirestore = querySnapshot
.docChanges()
.filter(({ type }) => type === "added")
.map(({ doc }) => {
const message = doc.data();
x = message;
return { ...message, createdAt: message.createdAt.toDate() };
});
appendMessages(messagesFirestore);
if (latestMessage != null) {
if (
new Date(
latestMessage["createdAt"]["seconds"] * 1000 +
latestMessage["createdAt"]["nanoseconds"] / 1000000
) >
new Date(
x["createdAt"]["seconds"] * 1000 +
x["createdAt"]["nanoseconds"] / 1000000
)
) {
latestMessage = x;
}
} else {
latestMessage = x;
}
});
return () => unsubscribe();
}, []);
I got the data from my database and i saved the oldest data in to latestMessage (for pagination) but the problem is that:
I declared my latestMessage out of my function like that:
let latestMessage = null;
export default function ChatTutor({ route }) {
...
}
And I passed my props to ChatTutor component (chatRoomId, username...) and according to that id, the room and its data are rendered. But the latestMessage always set some value and when i go to parent component and clicked another chatRoom, ChatTutor has a value of latestMessage's other value(oldest value). How can i set latestMessage null when i go to the parent ?
You can use useRef to store local mutable data (it would not participate in re-renders):
export default function ChatTutor({ route }) {
const latestMessage = useRef(null); // null is initial value
// ...
latestMessage.current = 'some new message' // set data
console.log(latestMessage.current) // read data
return <>ChatTutor Component</>
}
I started integrating websockets into an existing React/Django app following along with this example (accompanying repo here). In that repo, the websocket interface is in websockets.js, and is implemented in containers/Chat.js.
I can get that code working correctly as-is.
I then started re-writing my implementation to use Hooks, and hit a little wall. The data flows through the socket correctly, arrives in the handler of each client correctly, and within the handler can read the correct state. Within that handler, I'm calling my useState function to update state with the incoming data.
Originally I had a problem of my single useState function within addMessage() inconsistently firing (1 in 10 times?). I split my one useState hook into two (one for current message, one for all messages). Now in addMessage() upon receiving data from the server, my setAllMessages hook will only update the client where I type the message in - no other clients. All clients receive/can log the data correctly, they just don't run the setAllMessages function.
If I push to an empty array outside the function, it works as expected. So it seems like a problem in the function update cycle, but I haven't been able to track it down.
Here's my version of websocket.js:
class WebSocketService {
static instance = null;
static getInstance() {
if (!WebSocketService.instance) {
WebSocketService.instance = new WebSocketService();
}
return WebSocketService.instance;
}
constructor() {
this.socketRef = null;
this.callbacks = {};
}
disconnect() {
this.socketRef.close();
}
connect(chatUrl) {
const path = `${URLS.SOCKET.BASE}${URLS.SOCKET.TEST}`;
this.socketRef = new WebSocket(path);
this.socketRef.onopen = () => {
console.log('WebSocket open');
};
this.socketRef.onmessage = e => {
this.socketNewMessage(e.data);
};
this.socketRef.onerror = e => {
console.log(e.message);
};
this.socketRef.onclose = () => {
this.connect();
};
}
socketNewMessage(data) {
const parsedData = JSON.parse(data);
const { command } = parsedData;
if (Object.keys(this.callbacks).length === 0) {
return;
}
Object.keys(SOCKET_COMMANDS).forEach(clientCommand => {
if (command === SOCKET_COMMANDS[clientCommand]) {
this.callbacks[command](parsedData.presentation);
}
});
}
backend_receive_data_then_post_new(message) {
this.sendMessage({
command_for_backend: 'backend_receive_data_then_post_new',
message: message.content,
from: message.from,
});
}
sendMessage(data) {
try {
this.socketRef.send(JSON.stringify({ ...data }));
} catch (err) {
console.log(err.message);
}
}
addCallbacks(allCallbacks) {
Object.keys(SOCKET_COMMANDS).forEach(command => {
this.callbacks[SOCKET_COMMANDS[command]] = allCallbacks;
});
}
state() {
return this.socketRef.readyState;
}
}
const WebSocketInstance = WebSocketService.getInstance();
export default WebSocketInstance;
And here's my version of Chat.js
export function Chat() {
const [allMessages, setAllMessages] = useState([]);
const [currMessage, setCurrMessage] = useState('');
function waitForSocketConnection(callback) {
setTimeout(() => {
if (WebSocketInstance.state() === 1) {
callback();
} else {
waitForSocketConnection(callback);
}
}, 100);
}
waitForSocketConnection(() => {
const allCallbacks = [addMessage];
allCallbacks.forEach(callback => {
WebSocketInstance.addCallbacks(callback);
});
});
/*
* This is the problem area
* `incoming` shows the correct data, and I have access to all state
* But `setAllMessages` only updates on the client I type the message into
*/
const addMessage = (incoming) => {
setAllMessages([incoming]);
};
// update with value from input
const messageChangeHandler = e => {
setCurrMessage(e.target.value);
};
// Send data to socket interface, then to server
const sendMessageHandler = e => {
e.preventDefault();
const messageObject = {
from: 'user',
content: currMessage,
};
setCurrMessage('');
WebSocketInstance.backend_receive_data_then_post_new(messageObject);
};
return (
<div>
// rendering stuff here
</div>
);
}
There is no need to rewrite everything into functional components with hooks.
You should decompose it functionally - main (parent, class/FC) for initialization and providing [data and] methods (as props) to 2 functional childrens/components responsible for rendering list and input (new message).
If you still need it ... useEffect is a key ... as all code is run on every render in functional components ... including function definitions, redefinitions, new refs, duplications in callbacks array etc.
You can try to move all once defined functions into useEffect
useEffect(() => {
const waitForSocketConnection = (callback) => {
...
}
const addMessage = (incoming) => {
setAllMessages([incoming]);
};
waitForSocketConnection(() => {
...
}
}, [] ); // <<< RUN ONCE
i'm new to react-native and i'm using the library react-native-ble-plx to connect a Heart Sensor to my app and show it's values.
At this point i managed to connect it via bluetooth and store the information on a buffer where i can see with console.log(buffer), as shown in next picture.
My question is how can i render this information to the application? I don't know how to handle it from that buffer.
edit: I specifically want the 2nd value of that buffer (the one that normally is 70)
Here is the code:
scanAndConnect() {
this.manager.startDeviceScan(null,
null, (error, device) => {
this.info("Scanning...");
if (error) {
this.error(error.message);
return;
}
console.log(device.name)
//if (device && device.name == 'Inspire HR') {
if (device && device.name == 'HX-00043494') {
this.manager.stopDeviceScan()
device.connect()
.then((device) => {
this.info("Discovering services and characteristics ")
return device.discoverAllServicesAndCharacteristics()
}).then((device) => {
this.info("Setting notifications")
``return this.Async_setupNotifications(device);
})
.then(() => {
this.info("Listening...")
return this.setupNotifications(device)
}, (error) => {
this.error(error.message)
})
}
})`
}
async Async_setupNotifications(device) {
this.manager.characteristic = device.characteristicsForService("0000180d-0000-1000-8000-00805f9b34fb");
const buf = Buffer.from(characteristic.value, "base64");
console.log(buf);
console.log (buf[1]);
this.manager.characteristic.isNotifying = true;
this.manager.characteristic.isReadable = true;
if (error) {
this.error(error.message)
}
return ;
}
Thanks a lot for the help so far
Assuming that you are doing console.log from within the application, you need to access the data via buffer.data[1], it should give you the second value.
If the buffer is a global variable, simple example how to render in React Native Component:
import React from 'react';
import {View,Text} from 'react-native';
let buffer;
export default class YourComponent extends React.Component {
Async_setupNotifications = async (device) => { // call this method from within your code
this.manager.characteristic =
device.characteristicsForService("0000180d-0000-1000-8000-00805f9b34fb");
const buf = Buffer.from(characteristic.value, "base64");
console.log(buf);
console.log (buf[1]);
this.setState({pulse: buf.data[1]}); // assign the needed data to state
}
render() {
return(
<View>
<Text>{this.state.pulse}</Text>
</View>
)
}
}