nuxt 3 vue router data fetching and caching - vue.js

I'm really confused and stuck.
Here is what I think I understand so far in my code below.
PART A: before mounting it's gonna start trying to get datas on the serverside already.
PART B: when mounted on client, wait for datas to be ready, and do masorny layout.
PART C: since it is a component where I list all the datas, when the query changes, it will refresh the data with newly given queries.
Here is what I'm stuck at.
If I go to other page, for example detail page,
and comes back to list page by back and forward button,
no additional data fetching is sent, and scrollBehavior works as expected.
but when I change queries for other results,
then want to do back button to previous query result,
as written clearly in PART C, it fetches data again, saved scroll position is gone.
I'm not getting why in (1) nuxt automatically doesn't fetch same data
and how I should save fetch request and resotre scroll in (2) where I'm navigating within a single component.
const { data: designs, pending, refresh } = useLazyAsyncData('design',
() => fetchDesigns()
) // PART A
onMounted(async () => {
await until(pending).toBe(false).finally(() => masonry()) // PART B
watch(() => useRoute().query, async () => {
window.scrollTo({ top: 0 })
page.value = 1
await refresh()
await until(pending).toBe(false).finally(() => {
masonry()
})
}) // PART C
}

Related

Cannot Prevent Async on GraphQL Mutation and Recoil State updating process

I am working on a Chat Room portion of a larger React Native app and am facing issues with the updating of the page after a text has been sent. Currently, when the user compiles a text in a TextInput and hits the send button, it triggers a mutation that is supposed to add a message object to the chatroom model, which is linked to all of the users that are currently in the chatroom. It is then supposed to take the result from this mutation, which is the updated chatroom connected to all the users (the current user included obviously) and render its contents. It is intended to rerender the page after the activeThread atom is updated, since the page used the contents of activeThread to render everything on the page, new messages included. However, this occurs asyncronously and it tries to render a promise.... which you can't do. I've tried everything I'm capable of, using thens and awaits everywhere I could but JavaScript's giving me the middle finger pretty hard on this one. My code is below...
const handleSendMessage = async () => {
console.log(activeThread.id)
if (newMessage.length > 0){
return sendMessage({
variables: {
chatroomId: activeThread.id,
content: newMessage
}
}).then( async (newMessageThread) => {
await setUpdating(true)
await setNewMessage("")
await setKeyboardVisible(false);
await setActiveThread(newMessageThread)
}).then( async() => {
await console.log(activeThread)
await setUpdating(false)
})
}
else{
}
}
setUpdating is part of a useState. This is defaulted to false and when true the main page is not set to render. It is intended as a guard against attempting to render the promise. Didn't work, obviously
setNewMessage is defaulted to "" and is responsible for keeping track of the text the user has entered into the TextInput. Pretty irrelevant here.
setKeyBoardVisible is pretty self explanatory and also not necessary
setActiveThread is the heavy lifter here. Pretty much all of the contents rendered are going to be pulling data from activeThread, which is, again; a recoil state. For example, everything below looks essentially something like
<View>
<Text> {activeThread.someMethodOrValue} </Text>
</View>
I can only assume this has something to do with the async-ing. I have a console.log(error) statement in my backend GraphQL mutation resolver that would catch any errors there, and it's not triggering anything. The error I get everytime is the following...
TypeError: undefined is not an object (evaluating 'activeThread.chatroomName.split')
This error is located at:
in MessageThread (created by SceneView)
in StaticContainer
in EnsureSingleNavigator (created by SceneView)
in SceneView (created by SceneView)
in {/* keeps going down the stack you get the idea */}
[Unhandled promise rejection: TypeError: undefined is not an object (evaluating 'activeThread.chatroomName.split')]
at Pages/CommunicationPage/MessageThread.js:210:37 in MessageThread
Any solutions?
While the code I had still looks like it should work to me, we all know how finnicky code can be sometimes. What ended up working was separating the handleSendMessage function and the mutation, creating a whole new function for the mutation.
My new code looks like this...
const handleSendMessage = () => {
if (newMessage.length > 0){
handleMutation().then( (resolved) => { // This line fixed all the promise issues
setNewMessage("") // clears current message input
let newActiveThread = resolved.data.driverSendMessage.chatroom // creates new thread JSON from mutation data
console.log(newActiveThread) // testing out a different bug now lolllll
setActiveThread(newActiveThread) // Sets current thread to match the new one
// changes the entire user state, leaving all over threads untouched but updating the current one
let updatedThreads = [newActiveThread]
user.chatrooms.forEach( (chat) => {
if (chat.id == newActiveThread.id){
console.log("Skipping " + newActiveThread.chatroomName)
}
else {
updatedThreads.push(chat)
}
})
// changes the main recoil state
setUser({...user, chatrooms: updatedThreads})
})
}
else{
// Throw Error Handling for no input or just do nothing, we'll see
}
}
const handleMutation = async () => {
return sendMessage({
variables: {
chatroomId: activeThread.id,
content: newMessage
}
})
}

Pagination using Flatlist in React Native (Next/Previous)

Recently, I've been starting to learn React Native, and I stack with a pagination for my project. I am trying to create a Flatlist which will show only first ten items from data. Then when you click on the next arrow, it will display next different ten items instead of the first ten, and so on. And the same logic to the back arrow but only previous ten items. Here is my code. It works but with some issues. After the first clicking to the next ten items, it shows the same ten, but when you click again, it displays different ten. The back arrow seems that doesn't work properly at all. Could someone help me tweak my code to fix these issues? I will appreciate any help. Thank you!
https://snack.expo.dev/rWq_FLP8o
What you should do is add pageCurrent as a dependency in your useEffect. So every time pageCurrent changes the useEffect runs.
useEffect(() => {
(async () => {
const apiURL = `https://jsonplaceholder.typicode.com/albums?_limit=10&_page=${pageCurrent}`;
fetch(apiURL)
.then((res) => res.json())
.then((resJson) => {
setData(resJson);
});
})();
}, [pageCurrent]); // <-- Makes pageCurrent a dependency
Next make it so when you go back on page 1 it stays on page 1. This way you don't get negative pages or a zero page.
const handlePreviousPage = () => {
console.log("previous page clicked", pageCurrent)
// Do this so your page can't go negative
setpageCurrent(pageCurrent - 1<1?1:pageCurrent - 1)
}
Heres a full example (https://snack.expo.dev/#heytony01/keyboard-function-component-example)

React Native: Didn't rerender the component after data was updated

i have two screens one where the profile information is showing and another screen to edit the information. If i entered the first screen profile it's shows me the right data from the database. Then i move to the next screen where i can change the Information everthing worked so far. But if I go back to the previous screen i still see the old data. So there is no rerendering.
But if i navigate to the other screen that screen fetched the new data. and the call getCurrentUserProfile is executed
This ist the screen with the profile information about the user.
const ProfileScreen = props => {
const [userObj, setUserObj] = useState({});
useEffect(() => {
let mounted = true;
getCurrentUserProfile().then(user => {
if (mounted) {
setUserObj(user);
}
console.log(user)
});
return () => mounted = false;
}, []);
console.log("----b------") // This is only output on the first call
}
How can i fix this. Is there a way when in the database is something changed, so the component rerender and fetches the new data.
Thanks.
You are probably using #react-navigation package. So, if you want to refetch data when come back to the previous page, you can use https://reactnavigation.org/docs/use-is-focused on that “previous” page.
Just look at their docs, you will get the idea.
P.S: Usually we don't unmount components directly. In React Native, we use navigator to mount/unmount components

get relevant value from useSelector

I have two hooks:
const dispatch = useDispatch();
const response = useSelector(state => state.responseData);
And submit function:
const submit = () => {
dispatch(connectToServer(`${BASE_URL}user/signIn`, {
email: state.email,
password: state.password
}, 'post'))
if (response.data.token) <--- this data is 1 step late
//do smth
I see relevant data only in JSX elements when they are rendered, but there is no way to make a function based on this data, this data is 1 step late.
There's three issues here:
The connectToServer action is presumably doing some async work, so there's no way a response can have been updated by the time the next line runs
Even if you do await dispatch(connectToServer()), React may not have re-rendered yet
The callback can only reference the variable values that existed in scope at the time the callback was defined, ie, the render before the user clicked the "Submit" button.
You'll need to either:
Move the response handling into the thunk itself
Have the thunk retrieve the updated data from the store and return it / use it somehow
Move the response token handling into a useEffect and wait for the next re-render that has the right data

Calling 2 axios requests in 2 Vuejs components and sharing data using Vuex

I have 2 different components: Threads.vue and Posts.vue.
In Threads component there is a list of all threads and when clicking specific thread from the list it opens Posts.vue with listing all posts for that id_thread that is chosen in Threads.vue.
In Threads component there is a function that is called when user opens specific thread:
selectThread (val) {
let threadObjectToChange = {
id_thread: val,
thread_name: "",
};
this.component_threads.forEach(thread => {
if (thread.id === val) {
threadObjectToChange.thread_name = thread.title;
}
});
store.commit("changeSelectedThread", threadObjectToChange); // This is data of opened thread so I can use it in another component.
let urlToFetch = "http://localhost:3005/posts/thread/" + val;
axios.get(urlToFetch).then(function(response) {
let newPostsList = response.data.data;
if (newPostsList.length == 0) {
let emptyArr = [];
store.commit("changePosts", emptyArr);
return 1;
}
let arrResponse = response.data.data;
store.commit("changePosts", arrResponse);
})
.catch(function(error) {
// handle error
console.log(error);
})
.then(function() {
// always executed
});
},
This works great, the Posts.vue gets all the data that is shared using Vuex.
The problem starts when I refresh Posts.vue and there is no data (I understand why). Then I added axios request under created method in Posts.vue to get all posts for that id_thread (stored in vuex from Threads.vue) inside component. That works when I refresh the page, but now request is called in both Threads.vue and in Posts.vue when I'm clicking thread.
The Posts.vue renders faster than vuex updating its store so in that component axios is sending id_thread NULL because that id of a thread is written in Threads.vue after clicking.
It's a little bit complex to describe. What I want is latest posts from my database when I open Posts.vue by clicking thread in Threads.vue and by refreshing Posts.vue but sending the request just one time in both cases!
What do you suggest, what is the best approach for this? I need to refresh posts array in Posts.vue when opening that component but updating thread id in vuex is slow so it's sending null as a id.
In Posts.vue axios request is similar, there under created method I'm sending:
let urlToFetch = "http://localhost:3005/posts/thread/" + store.state.selectedThread.id_thread;
Is there any way to wait for store state variable to update and then trigger component render? What is the best practice?