On page refresh, destructuring props gives unhydrated values in componentDidMount - javascript

I have a strange error in my application that I haven't come across in React.
Basically, when the page refreshes, I grab certain parameters from the URL, make API requests on the queries and then populate my Redux state with the results.
For example:
async componentDidMount() {
const {
values1, values2, match,
} = this.props;
await getSingleData(match.params.id);
await getData(values1, values2);
}
So from the URL parameter, I grab the Id, then make an API request to get the data and store it in Redux. However, when I use the desctructured syntax, I get empty or null values. When using the destructured syntax, it seems like it references the empty Redux store before the data hydration, while the full reference gives the correct values after the data hydration.
When I use the full this.props.values1 and this.props.values2 reference, it works fine.
For example, this code, everything works like it's supposed to:
async componentDidMount() {
const {
values1, values2, match,
} = this.props;
await getSingleData(match.params.id);
await getData(this.props.values1, this.props.values2);
}
I'm confused as to why this is happening. As far as I understand, descructuring objects will have the same reference as the non-descructured counterpart. But it's not the case apparently. Any input?

Destructuring happens immediately when the function is called and at that time, this.props is null.
After call to await getSingleData(match.params.id) (which is run after the destructuring) this.props is filled.
Therefore, you are able to access this.props.value1.
Destructuring does not track or observe the original object's changes.

Related

cant map over and fetch the data present inside an array of objects

I had an assignment which i was not able to do since i was busy but out of curiosity I am trying to do it. I have this api https://dev-api.fitnessfuel360.com/app/home
from which i am trying to get the items and the data present in it.
I am currently trying to use Nextjs in which axios is being used.
this is what my code looks like as of now.
//Index.js file where Base_url is the api
const { items } = await axios.get(Base_URL);
return {
props: { items: items },
};
}
I am then passing Items to the the component where i want to render out the data present inside of the items array.
//my productSection.js component
<div>
{items.map((items) => (
<h1>{items.slug}</h1>
))}
</div>
However this gives me an error saying
Error: Error serializing `.items` returned from getServerSideProps in "/". Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
So how do i access the information inside of this Items array?
There were some points mentioned in the assignment as well like object has 2 properties type and data. Type key decides which component to render and data will be passed to to the component
and Code should not throw error on invalid types in json
I Have not used key to render out component before so this goes over my head.
Please understand that I am still a beginner so yeah, Please let me know what to do here and how to achieve this. Thank you for your time.
axios return a response object with a data property, try to change your code like this
const { data } = await axios.get(Base_URL);
return {
props: { items: data.items },
};
}

Returning a value from an Async Function. AWS/React

I'm trying to build a component that retrieves a full list of users from Amazon AWS/Amplify, and displays said results in a table via a map function. All good so far.
However, for the 4th column, I need to call a second function to check if the user is part of any groups. I've tested the function as a button/onClick event - and it works (console.logging the output). But calling it directly when rendering the table data doesn't return anything.
Here is what I've included in my return statement (within the map function)
<td>={getUserGroups(user.email)}</td>
Which then calls this function:
const getUserGroups = async (user) => {
const userGroup = await cognitoIdentityServiceProvider.adminListGroupsForUser(
{
UserPoolId: '**Removed**',
Username: user,
},
(err, data) => {
if (!data.Groups.length) {
return 'No';
} else {
return 'Yes';
}
}
);
};
Can anyone advise? Many thanks in advance if so!
Because you should never do that! Check this React doc for better understanding of how and where you should make AJAX calls.
There are multiple ways, how you can solve your issue. For instance, add user groups (or whatever you need to get from the backend) as a state, and then call the backend and then update that state with a response and then React will re-render your component accordingly.
Example with hooks, but it's just to explain the idea:
const [groups, setGroups] = useState(null); // here you will keep what "await cognitoIdentityServiceProvider.adminListGroupsForUser()" returns
useEffect(() => {}, [
// here you will call the backend and when you have the response
// you set it as a state for this component
setGroups(/* data from response */);
]);
And your component (column, whatever) should use groups:
<td>{/* here you will do whatever you need to do with groups */}</td>
For class components you will use lifecycle methods to achieve this (it's all in the documentation - link above).

Sequential dependent functions in Vuex action

I'm currently building a Vue app that consumes data from the Contentful API. For each entry, I have a thumbnail (image) field from which I'd like to extract the prominent colours as hex values and store them in the state to be used elsewhere in the app.
Using a Vuex action (getAllProjects) to query the API, run Vibrant (node-vibrant) and commit the response to the state.
async getAllProjects({ commit }) {
let {
fields: { order: order }
} = await api.getEntry("entry");
let projects = order;
projects.forEach(p =>
Vibrant.from(`https:${p.fields.thumbnail.fields.file.url}`)
.getPalette()
.then(palette => (p.fields.accent = palette.Vibrant.hex))
);
console.log(projects);
// Commit to state
commit("setAllProjects", projects);
}
When I log the contents of projects right before I call commmit, I can see the hex values I'm after are added under the accent key. However, when I inspect the mutation payload in devtools, the accent key is missing, and so doesn't end up in the state.
How do I structure these tasks so that commit only fires after the API call and Vibrant have run in sequence?
You cannot add a property to an object in Vue and have it be reactive; you must use the Vue.set method.
Please try replacing that forEach block with the following, which adds the new property using Vue.set:
for (i=0; i<projects.length; i++)
Vibrant.from(`https:${projects[i].fields.thumbnail.fields.file.url}`)
.getPalette()
.then(palette => (Vue.set(projects[i].fields, accent, palette.Vibrant.hex)))
);
UPDATE: changing the format from forEach to a conventional for loop may be gratuitous in this case, since the assignment being made is to an object property of projects and not to a primitive.
I'm not spending a lot of time on StackOverflow, and if the above answer works, I am happy for you indeed.
But I expect from that answer you will get console warnings telling you not to mutate state directly.
Now when this happens, it's because while Vue.set(), does in fact help Vue understand reactively a change has been made, potentially deeply nested in an object.
The problem here is that since you are looping the object, changing it all the time, the commit (Mutator call) is not the one changing state - Vue.set() is actually changing it for every iteration.

React setState() not setting state on certain items

I'm new to React and have some pretty simple code that is acting strange (I think).
The main app fetches a list of blog posts from a server, then passes them through props to a child component which spits the list out. By default, I'm trying to make the posts only show a preview like a title, and each post will have a state attached to it so I can keep track of which ones are fully shown or previewed.
I have the states set up like this:
const [posts, setPosts] = useState([])
const [postFullView, setPostFullView] = useState([])
The list initially is rendered as an empty list so nothing gets returned. When the data fetch finishes, it re-renders with all the posts.
I use useEffect for this in the child component:
useEffect(() => {
console.log('render') //Just to verify this got called
setPosts(props.posts) //Logs empty array 3 lines down,
//setPosts([4,5,6]) //Works fine, gets logged as [4,5,6]
console.log(props.posts) //Logs an array of 32 objects - so props is clearly not empty
console.log(posts) //Logs empty array as if setPosts did nothing, but logs [4,5,6] if I comment out setPosts(props.post) and use setPosts([4,5,6])
setPostFullView(posts.map(post => {return {id: post.id, view: false}}))
console.log(postFullView) //Will be empty since posts is empty
}, [props])
Hopefully, I explained clearly what I'm confused about - I can setState using a hard-coded array, but passing in props.posts does not do anything, even though it has content.
There is nothing wrong about your code, and the reason console.log(posts) spits empty array, it because setPosts(props.posts) is async call and not executed immediately, but tells react it should render again with new value for state.
Sometimes, like in your hardcoded array case, the code will work "fine", but it not guaranteed, for sure in production when code executed faster
yea its nothing wrong because the setState api is async did not show the changes in log but code is work properly and also if you need to check your state you can use React developer Tools extension for browser too see the state status
https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en
Actually, your first issue is about understanding the state changing and the re-rendering on ReactJS. why you do not use the first initial state in the first render just like below:
const YourComponent = ({ posts: initialPosts }) => {
const [posts, setPosts] = useState(initialPosts);
Also, there is no need to have the first line you can use it exactly on the second line initializing, like this:
const YourComponent = ({ posts: initialPosts }) => {
const [postFullView, setPostFullView] = useState(
({ id }) => ({ id, view: false }) // es6 arrow function with using restructuring assignment and returning the object
);
After all, when you are using a useEffect or other hooks APIs, please add the specific internal state or prop name to dependencies, no put all the props in the array of dependencies, it caused bad costs and make your project slow to run.

Getting primitive values from mobx observable in reactjs

I have a observable store that is being based via props to components that need it.
Console logging from components does show store as expected, but only if I stick to the whole store. Once I start chaining into it I get undefined.
Base store
export let TutorStore = observable({
Tutor: {},
Queue: [],
QLength: null
});
Component logging
checkBtn = () => {
console.log(this.props.tutorStore);
};
Result TutorStore in Console as expected
All the correct tutorStore objects are there, with the right data, as expected.
But if I try to chain into a object there are no values attached to it, not behaving as expected.
checkBtn = () => {
console.log(this.props.tutorStore.Tutor);
};
Result TutorStore.Tutor no values
I've tried messing around with mobx's toJS method, but it seems unreliable at best.
Considering just assigning the appropriate object to components state, but that defeats the purpose of having a store.
The data is there, so how do I access it?

Categories