I wouldn't call this example a "derived state example". This is two different states having a relationship. Derived state means a value that is directly calculatable from other states. Selected user cannot be calculated from other states (users), it simply requires a user input ( outside event). This is still a good example of "states with relationship, should be updated together" example.
It's essentially the data normalisation problem from relational databases. Don't store the same data in 2 places or risk inconsistencies. Instead store a reference to a single location.
@@robertsandiford6223 Yes. But in database layer, there is a guarantee of unique ID. For frontend, you don't always have that. Assume that "id" doesn't exist, or every field can change. For example, next to the "Increment" button, we have a text input, which changes the "id". Now, we have the same initial bug. The ultimate solution is to update them both at the event handler ( or use something with reducer, which does the necessary updates in one place through an action). I would give a "derived state" example like this. Assume there are 2 inputs, one is "firstName", the other one is "secondName". And you hold the "fullName" in state. "fullName" is fully derivable from "firstName" and "secondName". It doesn't need to be stored as state. It should just be calculated whereever you need.
@@cmlttnts4906 IDs are not guaranteed in databases, it's a design choice to add them. Generally you can choose to ID your state data if you want to. You can do parallel updates in event handlers, and that is fine for simple tasks. It's not recommended in large DBs because of the risk of making a mistake - in a simple component it would be ok, but might cause trouble if complexity grows. You example with first name and last name matches database normalisation principles - the fields are the single store and value, and they are referenced to build to composite value when required, the same as selecting from multiple tables in relational databases.
my app works just fine coz I put the selectedID in the dependency array of the useEffect where I fetch API and set the state.... but now I see how silly my solution is while I can just use your simple one line of code to define selected state.....changed it immediately! thank you!!!
I think derived state doesn't need to use the useState at all, it should be calculated based on users state. So I'm not sure is this a truely example of derived state.
If you have a list of items that doesn't change as often, you could simply save the index of the item in your array. So you don't have to loop through the array to find the correct item
Yes, the structure here isn't optimal in general. With the IDs, which should be unique, simply being a property within the objects within an array, as opposed to being the indices or keys in an array or object, we're also missing out on directly addressing them. I wonder if that's common practice if you're just pulling objects from your DB?
Even if the array changes often.. Just using a setSelectedIndex in his selectUser, that's where the find has to be done (as findIndex()), once only when the id changes.. And set to 0 when the usersArray changes to not have a wrong index (or again find the id in the new array). This saves the overhead of useMemo too ;)
Thanks. This is a pretty common but annoying possible bug. This is another reason why I love svelte so much, just use $: and it becomes reactive, derived or whatever you want.
You actually "break" the reference to that object when you do return { ...user, etc }, this creates a new object with the same contents, therefore: new reference. You should apply the increment directly to user and return user to keep it working straight. But if you work with references you open up to a lot of problems if you dont really comprehend how references work. Unless you dominate or are trying to dominate references, use native values to avoid issues.
@@marios594 I think he can, mutating value directly of the item in the array does the same thing, but in React.StrictMode, state updater will run twice. we can get rid of strict mode or use state value (directly from users and set it like [ ...users ]) instead of state updater. still, I'm not familiar with react and I don't know if this is the appropriate way.
Instead of useMemo maybe put the users in a Map using the ID as the key, or maybe save the array index of the selected user in the state rather than the ID.
@@ivanche8051 useMemo can only memorize if the function has already run once. If we select another user that has never been selected, we still have to go through the whole list of users to find the id. Using id as key in this case can search quicker.
what he didn't explain is WHY "selectedUser" and the user that its age is incremented are not the same reference? the reason is that when incrementing one user's age we mutate that user it by destructuring .. so we're basically creating a new one : return { ...user, age: user.age + 1 }
@@alecodes no, it’s not copied. SelectedUser references the same exact object which exists in the array of users. The problem is in Increment function, where he replaces the object in the array with a new object. If he just mutated the original object on age increment, everything would work fine, and I’d actually like this solution more.
Just copy my reply to comment so new devs might be aware of. There are few problems with the video example and solution. For the example itself, check the top comments with highest vote regarding the definition of derived state. I want to point out the problems with that solutions from some of the comments and the video, the video example is really tricky. First, some of comments say use array index so you don't need to use array.find(), it's questionable/wrong to use array index id as selectedUserId cuz the array index will cause bug once the array size changed hence index is not longer unique. If u pretty sure the size and position are static under any circumstances. This approach is probably the most performant and simplest solution Second, use the unique user id as the find id. This is the video solution, it's good but when the size is large, it will cause performance issue as the video claimed later. However, the useMemo() in this case, doesn't solve the performance issue because when you update the selection, it will modify the array hence cause useMemo() to rerun. the useMemo will be useful only when the parent container is changed but at the same time you don't want to re-render the current selection list. But for this example with its context, the useMemo() solution is simply wrong. the trouble array.find() will still run regarless. So what's my answer? I would say if the list is large, the performance is important, the initial code is fine. You just need to update both selectedUser & users when you call the update, and only update selectedUser if the user id is matching the updated user in users list. You don't even need to use the array.find() and useMemo() in the first place, it also reduces the mental overhead of using memo hooks. The root issue of his solution is because the "issue" example he provided doesn't match the definition of derived state. It just looks like it, that's why I called it tricky. If the example is derived state, you shouldn't need useMemo() most likely.
Great lesson! This is a source of many bugs... I've tried solving it in another way (as a practice). My goal was to fix the bug and improve performance, and this is what I came up with. export default function UserLop() { const [users, setUsers] = useState([ { id: 1, name: "Kyle", age: 27 }, { id: 2, name: "Sally", age: 32 }, { id: 3, name: "Mike", age: 54 }, { id: 4, name: "Jim", age: 16 }, ]); const [selectedUserIndex, setSelectedUserIndex] = useState(); function incrementUserAge(id) { setUsers((currUsers) => { return currUsers.map((user) => { if (user.id === id) { return { ...user, age: user.age + 1 }; } return user; }); }); } return (
Select User:{" "} {selectedUserIndex == null ? "None" : `${users[selectedUserIndex].name} is ${users[selectedUserIndex].age} years old`}
{users.map((user, index) => { return (
{user.name} is {user.age} years old.{" "} { incrementUserAge(user.id); }} > Happy birthday {" "} { setSelectedUserIndex(index); }} > Select
@@hd33444 Well lets say you have 3 item list. So indexes from 0 to 2. Select 2nd item, so index = 1, thats in state now. Now if you would have an option to delete items, you could delete 1st item, and your currently selected item, previously with index == 1, now actually has index == 0 (its the 1st element now). But in your state there would still be, that selected index is index = 1. However, now that would actually be referencing the previously 3rd item in the list. In general using indexes to point to elements in the array is not such a great idea if you have dynamic lists (using .map and its 'index' argument as 'key' prop as well) In my experience i found that just having the objects with 'id' key/value and referencing those is the most simple and reliable way to identify them in the list ;)
well, good solution but this solution as you said had bad performance since you need to loop in the users' array to find the selected user, 1)alternative solution is to store the users in an object instead of an array like this: const [users, setUsers] = useState({ 1: { name: "keyle", age: 27, }, 2: { name: "sally", age: 30, }, 3: { name: "mike", age: 25, }, }); and now the selected state can be any id (1,2,3) and you can access the selected user object with o(1) and you can make an array of users id to keep track of their sequence like this: const sequence = [2 , 1 , 3] 2)another solution is to keep the users state an array of obj like you did but instead of saving the user id in the selected state, we can just save it is index (since the order does not change) thank you for the video amazing
Looping over the array every time the component re-renders is bad, but by using useMemo you get rid of this problem, and you only loop when you need to. There's no need to fundamentally change your code by using objects or some other ways
@@mrhenry60412 Then we´d still have to loop through the users to find the one that is selected. It would also add complexity since we´d have to unselect one user when the next one is selected.
@@GuthixCss well, I just don't think that creating a hook to store id is necessary, when you can manage all user-related info together. What if you need to deal with some functionality like hiding selected user or changing the font color of selected user at the same time?
If we can store the array position in selectedUser state and on update of user in useEffect we can use this array position like user[selectedUser] then it will be far easier and faster compilation of code
but also more prune to bugs, to get a negligable performance gain unless you have a wierd case. Usually react does not re-render that often so the linear-search is not done very often and in most cases the list is short enough to not matter anyway. And in those rare cases a useMemo is probably fast enough and less prune to you making bugs. (if you store the index you need to be 100% sure the list never changes - and what if we added features for add and delete users later? - you would then have to make sure that you also update the index on those operations. Easy to forget, and would trigger a bug.
I like Kyle's tutorial course. You explain the concepts clearly and make the courses comprehensive. You share the knowledge, the keys to write simple and high-quality code without reservation. I bought some online React and css courses. I think Kyle's course is worth the price. Thanks.
Thank you for this tutorial. It helped me understand the concept. Waiting for more such videos on React. A request if you could tutorial on react testing like Jest and React Testing Library
LMAO I kid you not I was just learning about this in the Scrimba React course. What a coincidink that this would be uploaded only a couple hours prior, and show up as the first thing on my youtube feed.
2 года назад+1
your useMemo will get re generated every time you click "increment" because you are doing a .map and that's generates a new array so the depenecy array of useMemo will be a new one so your useMemo makes no sense 🙂
Sorry maybe some here asked the same question, how is the name of the extension you are using to show the error in that big modal in the app instead of going to the console??? please a link to it or the name to search it please?
This is a profound example of by-reference intuition from the real world blowing up on a learning coder. Does Javascript make this any harder, by being subtle about objects vs primitive value ?
Oh, it's an easy trap to fall into for sure. I see "reference types vs value types" and mutation tripping up people all the time. But I wouldn't say objects vs primitives is compounding the issue much. You need to keep track of what you're referencing in any case
Dear Bro, me install just react version newly but take some problems,, when i use input tag then page show me only "Blank Page" plz help me.... i working last 5 days but no any solution...plz make video not show me blank page..me waiting your video. i am from pakistan
Emmm if we know, when the current age is increment, cant we then update current user? incerementAge -> ...changing data in users... -> set current user by inceremening with new date
To go a step further into performance, I'd keep the users state as an object instead, where key is the id and value is the user, so your derived selectedUser state wouldn't have to loop (find) throught the users array every time selectedUserId changed, but actually access it directly e.g. const selectedUser = useMemo(() => users[selectedUserId], [selectedUserId, users]);
When he uses the "users" array to keep track of changes with the useMemo() call.... 1. What is react checking exactly behind the scenes? Just the pointer to the array "users" or a more deep comparison (things like length, changing the objects (per se or sub-properties) inside each index, etc) ? 2. Also isn't "users" being re-created on each render cycle? Wouldn't useMemo going to be doing the find() operation every single time?
yeah, the example is more about having copies of data, in those cases you need to make sure you update all the copies. It is not really derived data since you can not derive selectedUser from the state before he added selectedUserId to the state.
Very important lesson in your software class.. but who in the interpreted react/js world cares about storage? As far as I know it's always important to keep redundancy low, but it's not always the best tradeoff. At some degree of complexity for a specific calculation you should avoid recalculating specific values time over time, rather than just looking them up in the storage. If your use case isn't time-sensitive as it is the case for most basic applications I usually limit this to a depth of 42 additional operations in comparison to a simple lookup in storage, it's ideomatic just for simplicity and to have this as a reminder if time matters 😅 especially in the SQL area with less 'intelligent' DBMS systems - often found in cluster cases - this helps out to improve the timing overall. Most of the time I would recommend using higher structures represented in arrays or maps/objects to keep track of changes more granularly, but that's case specific, as every decision you make in your life.
If users list was huge, isn't it inefficient to filter through them? I would suggest saving the users as object with keys as ids, and then getting the selected user is o(1)
That is also good solution, but then to render users, you have to create array from an Object which would also be O(N) on each render. Or again we could useMemo on that object and create new array from that object only when users change. But again, we are down to same thing, either we have create array from users object or that filter derived state. I guess filter derived state would be faster, as filtering can not always be in worst case, sometimes we can find user ar first place only, and it’s average case would be finding user in mid of an array. But on other hand we have to create whole array from Object with Object.keys, values or entries function etc. Which is always exactly O(N).
Good argument,. you can have list of ids which can represent the order of rendering, and objects of items in object with id as keys. Rendering list through the ids array, selecting the object from the objects state.
@@ElektrykFlaaj yeah but when you add pagination and don't have all the data on the UI you lose alot of abilities, like searching on the frontend, and you'll have to do backend search and sort..
@@amitneuhaus2989 yes I guess, depends on use case. If users aren’t going to change much rather selected user would, than using object as initial state in fine, considering we are memoizing the render array with useMemo. Otherwise, if users data is getting changed often, we can use this array approach shown in the video.
Nice video, but I have a question, if always change user list, useMemo is worth?, because it added a validation to change its value and always values change, I prefer to acces with index but it not always work
What is the point of useMemo at this case, because if we increment any user age or select new one, there still be .find through list (because there is no other case for rerender), instead we could store key - value object where key is user id and value is user, so we would not need iteration
This is a very simplified example so that it’s easier for beginners to understand. He mentions in the video that this is useful if you have other state or props making the component rerender, like theme for example.
This is jus an example, Real world app would have lot of other states which could cause render. And using object to store data, with userId as key do look okay on surface. But it also has same problem. Cause React cannot render object, you have to create array of users form users object using Object methods like Object.keys, entries etc on every render which would always he O(N) and than map over that array to show users. Basically we are back to same problem, either use state as array of objects and avoid creating array in every re-render ans use filter to find sleeted item. Or use object of users with keys being userIds and create array from this object to map over in every re-render
@@anniecenter You if have list with 1000 users and change age to one of them - your array is changing, so it will be .find again through 1000 users even if you did not change selected users and useMemo won't help you with that.
@@mikesummer670 again we are storing two states for one data source.. Exactly the problem this video tried to solve. In real world scenarios, we would have feature of adding, deleting users. You would have to manage state at both the places with this approach. But yeah, dependent on use case, if problem statement is not much complex, storing an array for ids differently could be used
You will face another issue in this case. You will need to find currently selected user and set selected to false which in this example is not a big problem, but if you start keeping track of different object states and behaviors inside an object itself it could become a huge pain to update and maintain.
useMemo() doesn't have any use in this component because it has both the state variables in the dependency list! So, whatever we do in this component, it'll run the loop through the whole users list to find the selected user
because it's not "simply", you're increasing complexity and decreasing readability. in general if it's not necessary to store something in state then you probably shouldn't store it in state
Why dont make an extra selectedUser field in the users state ,and when u update user age u update age of user on users array and the age of selectedUser the same time.
What's the possible/potential bug here? I don't see any issue with storing the selected user object in state, please provide a concrete example of a potential bug from applying this pattern as opposed to "bad things can happen".
He explained it, when he incremented the age of the user on the list, the age of the selected user wasn't changing, because it was stored as a separate state. It is really a bad practice to store the object in a state, because if you do that, you'll have a duplicate state (the user on the list and the same user on the selectedUser state), and you'd have to update both states manually when some attribute of the user changes (in this case, the age)
Can anyone help me to understand, After memorizing the function to derive selected user Kyle said that, It will help to boost performance if there are bunch of other states - which makes sense, But he also said that if we have 1000s of users it will help with performance, If users are in dependacy array, meaning that function is going to run and iterate through every user any time user's value change, how would it help here?
without memoizing, every time you click "increment" the 'selectedUser" function would run each time. With useMemo, only when you click "select" the function will run, thus running only when you need it to, and not whenever the component rerenders
iirc the beta.reactjs docs talk about this. Basically never store the same data multiple times in state. Only one copy. Best solution is selectedUser to be an id or index
As an angular developer forced into react, it seems to me that react is prone to bugs. You have to get everything right, every time, or you get bugs and/or poor performance.
Great video, one thing I would say about react hook: I just love it! Before I used to use react use state but once I know how hook work, it's just so much better :)
2:22, here one way to solve this is: instead of returning a new object in the map, you can do: if(user.id === id) { user.age += 1; } return user; this will solve this reference inequality issue. However, you should keep this in mind that you are mutating the object.
@@narendrareddyyarramreddy2007 your justification is partially incorrect because that's not how re-render is happening. The array reference is what is being tracked off from state and since .map() returns a new array, this new array and old array is referentially different so react will cause a re-render regardless
@@awekeningbro1207 If I want to return a new object, I would prefer the conventional way of spreading everything and changing required ones.. It looks more cleaner 🤔
@@awekeningbro1207 for this example you are right, array change is causing re-render. But consider a case, where each row is a new User component which takes user and renders it’s age and that User component is memoized with memo() HOC as well for performance reasons, as there can be tons of rows, and re-rendering every row just so because some parent’s different state is changed or data of some single row is changed, is not feasible. Now here, each User component only have user prop, based on its change the User component can re-render. But now with your approach, each user object is mutated on age change, thus User component will not re-render, causing old age to render on UI. Yeah, you could say that in current case, that is not the problem, but we should write code in a correct way, which can be easily extended. Thus we should follow React’s principal of not mutating state. Instead of writing React’s code in non-recommended way, and then trying to figure out in future why certain thing is not working on extended features or codebase!
You can compare the state and the variables during a test, but it isn't a good thing to test, you should test the funcionalities themselves and how the app is behavioring. For instance in this case the test should be that "if i increment a selected user, both lables should show the same information".
I wouldn't call this example a "derived state example". This is two different states having a relationship. Derived state means a value that is directly calculatable from other states. Selected user cannot be calculated from other states (users), it simply requires a user input ( outside event). This is still a good example of "states with relationship, should be updated together" example.
I agree with you.
It's essentially the data normalisation problem from relational databases. Don't store the same data in 2 places or risk inconsistencies. Instead store a reference to a single location.
@@robertsandiford6223 Yes. But in database layer, there is a guarantee of unique ID. For frontend, you don't always have that.
Assume that "id" doesn't exist, or every field can change. For example, next to the "Increment" button, we have a text input, which changes the "id". Now, we have the same initial bug.
The ultimate solution is to update them both at the event handler ( or use something with reducer, which does the necessary updates in one place through an action).
I would give a "derived state" example like this. Assume there are 2 inputs, one is "firstName", the other one is "secondName".
And you hold the "fullName" in state. "fullName" is fully derivable from "firstName" and "secondName". It doesn't need to be stored as state. It should just be calculated whereever you need.
@@cmlttnts4906 Thanks for your sharing . That’s really helpful and clear.
@@cmlttnts4906 IDs are not guaranteed in databases, it's a design choice to add them. Generally you can choose to ID your state data if you want to.
You can do parallel updates in event handlers, and that is fine for simple tasks. It's not recommended in large DBs because of the risk of making a mistake - in a simple component it would be ok, but might cause trouble if complexity grows.
You example with first name and last name matches database normalisation principles - the fields are the single store and value, and they are referenced to build to composite value when required, the same as selecting from multiple tables in relational databases.
my app works just fine coz I put the selectedID in the dependency array of the useEffect where I fetch API and set the state.... but now I see how silly my solution is while I can just use your simple one line of code to define selected state.....changed it immediately! thank you!!!
I think derived state doesn't need to use the useState at all, it should be calculated based on users state.
So I'm not sure is this a truely example of derived state.
If you have a list of items that doesn't change as often, you could simply save the index of the item in your array. So you don't have to loop through the array to find the correct item
Yes, the structure here isn't optimal in general. With the IDs, which should be unique, simply being a property within the objects within an array, as opposed to being the indices or keys in an array or object, we're also missing out on directly addressing them.
I wonder if that's common practice if you're just pulling objects from your DB?
Even if the array changes often.. Just using a setSelectedIndex in his selectUser, that's where the find has to be done (as findIndex()), once only when the id changes.. And set to 0 when the usersArray changes to not have a wrong index (or again find the id in the new array). This saves the overhead of useMemo too ;)
I agree. Accesing a map/array element with index is absolutely quicker than looping through a list of element.
Thanks. This is a pretty common but annoying possible bug. This is another reason why I love svelte so much, just use $: and it becomes reactive, derived or whatever you want.
You actually "break" the reference to that object when you do return { ...user, etc }, this creates a new object with the same contents, therefore: new reference. You should apply the increment directly to user and return user to keep it working straight.
But if you work with references you open up to a lot of problems if you dont really comprehend how references work. Unless you dominate or are trying to dominate references, use native values to avoid issues.
indeed
Yeah mutating the original user object would be the best fix here
I'm not a React expert yet it caught my eyes too, it was strange but I told myself I just made up. Nevertheless I guess I my intuition was right.
what are you talking about? You cant mutate states
@@marios594 I think he can, mutating value directly of the item in the array does the same thing, but in React.StrictMode, state updater will run twice. we can get rid of strict mode or use state value (directly from users and set it like [ ...users ]) instead of state updater. still, I'm not familiar with react and I don't know if this is the appropriate way.
3:02 - Try to press `F2` for renaming const in whole document. It really helpful sometimes.
These small tips come from experience. I don't have any 😭
Thanks so much 🙏 I just removed unnecessary code and increase performance in my project
Instead of useMemo maybe put the users in a Map using the ID as the key, or maybe save the array index of the selected user in the state rather than the ID.
why not useMemo? That’s what it’s made for
@@ivanche8051 useMemo can only memorize if the function has already run once. If we select another user that has never been selected, we still have to go through the whole list of users to find the id. Using id as key in this case can search quicker.
Why did you pass reference type variable as a dependency to use Memo
It will change on every render and useMemo will just become inefficient
what he didn't explain is WHY "selectedUser" and the user that its age is incremented are not the same reference?
the reason is that when incrementing one user's age we mutate that user it by destructuring .. so we're basically creating a new one : return { ...user, age: user.age + 1 }
It's because the user's state is copied into selectedUser, so they have nothing to do with each other
Thanks, I was wondering why he didn't address that. Sharing the reference would be a simple fix, although it also has its own downsides.
@@alecodes no, it’s not copied. SelectedUser references the same exact object which exists in the array of users. The problem is in Increment function, where he replaces the object in the array with a new object. If he just mutated the original object on age increment, everything would work fine, and I’d actually like this solution more.
@@uriniumintestor7302 ah but you can't mutate it like that in react cause react is written by crazy people
@@feritperliare2890 I don’t see why not. They use “setState” to set the result state again. Why do you think it wouldn’t work?
Just copy my reply to comment so new devs might be aware of. There are few problems with the video example and solution.
For the example itself, check the top comments with highest vote regarding the definition of derived state.
I want to point out the problems with that solutions from some of the comments and the video, the video example is really tricky.
First, some of comments say use array index so you don't need to use array.find(), it's questionable/wrong to use array index id as selectedUserId cuz the array index will cause bug once the array size changed hence index is not longer unique. If u pretty sure the size and position are static under any circumstances. This approach is probably the most performant and simplest solution
Second, use the unique user id as the find id. This is the video solution, it's good but when the size is large, it will cause performance issue as the video claimed later.
However, the useMemo() in this case, doesn't solve the performance issue because when you update the selection, it will modify the array hence cause useMemo() to rerun. the useMemo will be useful only when the parent container is changed but at the same time you don't want to re-render the current selection list. But for this example with its context, the useMemo() solution is simply wrong. the trouble array.find() will still run regarless.
So what's my answer?
I would say if the list is large, the performance is important, the initial code is fine.
You just need to update both selectedUser & users when you call the update, and only update selectedUser if the user id is matching the updated user in users list. You don't even need to use the array.find() and useMemo() in the first place, it also reduces the mental overhead of using memo hooks.
The root issue of his solution is because the "issue" example he provided doesn't match the definition of derived state. It just looks like it, that's why I called it tricky.
If the example is derived state, you shouldn't need useMemo() most likely.
Great lesson!
This is a source of many bugs... I've tried solving it in another way (as a practice).
My goal was to fix the bug and improve performance, and this is what I came up with.
export default function UserLop() {
const [users, setUsers] = useState([
{ id: 1, name: "Kyle", age: 27 },
{ id: 2, name: "Sally", age: 32 },
{ id: 3, name: "Mike", age: 54 },
{ id: 4, name: "Jim", age: 16 },
]);
const [selectedUserIndex, setSelectedUserIndex] = useState();
function incrementUserAge(id) {
setUsers((currUsers) => {
return currUsers.map((user) => {
if (user.id === id) {
return { ...user, age: user.age + 1 };
}
return user;
});
});
}
return (
Select User:{" "}
{selectedUserIndex == null
? "None"
: `${users[selectedUserIndex].name} is ${users[selectedUserIndex].age} years old`}
{users.map((user, index) => {
return (
{user.name} is {user.age} years old.{" "}
{
incrementUserAge(user.id);
}}
>
Happy birthday
{" "}
{
setSelectedUserIndex(index);
}}
>
Select
);
})}
);
}
What do you think?
this will bug out if you have a dynamic list, as objects under indexes can change
@@k0si hey man, would you be able to explain what you mean by that some more??
@@hd33444 Well lets say you have 3 item list. So indexes from 0 to 2. Select 2nd item, so index = 1, thats in state now. Now if you would have an option to delete items, you could delete 1st item, and your currently selected item, previously with index == 1, now actually has index == 0 (its the 1st element now). But in your state there would still be, that selected index is index = 1. However, now that would actually be referencing the previously 3rd item in the list. In general using indexes to point to elements in the array is not such a great idea if you have dynamic lists (using .map and its 'index' argument as 'key' prop as well)
In my experience i found that just having the objects with 'id' key/value and referencing those is the most simple and reliable way to identify them in the list ;)
@@k0si Ahhh ok yeah that makes sense. Thanks for the answer!
Now I wonder if I ever did this. It seems like a obvious mistake, but of course it can get you in some weird troubles.
Oh wow I thought after a week or 2 of learning react, I got the hang if it but man did I find this video hard to keep track of
Great video, very helpful !!
well, good solution but this solution as you said had bad performance since you need to loop in the users' array to find the selected user, 1)alternative solution is to store the users in an object instead of an array like this:
const [users, setUsers] = useState({
1: {
name: "keyle",
age: 27,
},
2: {
name: "sally",
age: 30,
},
3: {
name: "mike",
age: 25,
},
});
and now the selected state can be any id (1,2,3) and you can access the selected user object with o(1)
and you can make an array of users id to keep track of their sequence like this:
const sequence = [2 , 1 , 3]
2)another solution is to keep the users state an array of obj like you did but instead of saving the user id in the selected state, we can just save it is index (since the order does not change)
thank you for the video amazing
awesome way of thinking. i have to master this way of thinking because usually i have to deal with performance issues in my apps.
Looping over the array every time the component re-renders is bad, but by using useMemo you get rid of this problem, and you only loop when you need to. There's no need to fundamentally change your code by using objects or some other ways
what if we just add a state in users like this:
const [users, setUsers] = useState([
{
id: 1,
name: "keyle",
age: 27,
isSelected: false,
},
{
id: 2,
name: "sally",
age: 30,
isSelected: false,
},
{
id: 3,
name: "mike",
age: 25,
isSelected: true,
},
]);
@@mrhenry60412 Then we´d still have to loop through the users to find the one that is selected. It would also add complexity since we´d have to unselect one user when the next one is selected.
@@GuthixCss well, I just don't think that creating a hook to store id is necessary, when you can manage all user-related info together. What if you need to deal with some functionality like hiding selected user or changing the font color of selected user at the same time?
If we can store the array position in selectedUser state and on update of user in useEffect we can use this array position like user[selectedUser] then it will be far easier and faster compilation of code
but also more prune to bugs, to get a negligable performance gain unless you have a wierd case. Usually react does not re-render that often so the linear-search is not done very often and in most cases the list is short enough to not matter anyway. And in those rare cases a useMemo is probably fast enough and less prune to you making bugs.
(if you store the index you need to be 100% sure the list never changes - and what if we added features for add and delete users later? - you would then have to make sure that you also update the index on those operations. Easy to forget, and would trigger a bug.
Great videos man thanks for all your effort!
Beautiful explanation!
Nice Vid. Still happens in my code a lot. Great to have this explained and simplefied :)
I like Kyle's tutorial course. You explain the concepts clearly and make the courses comprehensive. You share the knowledge, the keys to write simple and high-quality code without reservation. I bought some online React and css courses. I think Kyle's course is worth the price. Thanks.
Yep I like Vue approach more. Computed props are more straightforward there. In React, I do it this way as you showed. useMemos everywhere.
Thank you for this tutorial. It helped me understand the concept. Waiting for more such videos on React. A request if you could tutorial on react testing like Jest and React Testing Library
Awesome 👍... Thanks so much
amazing video, will definitely keep in mind from next time.
LMAO I kid you not I was just learning about this in the Scrimba React course. What a coincidink that this would be uploaded only a couple hours prior, and show up as the first thing on my youtube feed.
your useMemo will get re generated every time you click "increment" because you are doing a .map and that's generates a new array so the depenecy array of useMemo will be a new one so your useMemo makes no sense 🙂
I think states having relationship with each other should be treated with useReducer.. as you need an single state but multiple ways to update it...
Honestly Kyle, I was going to read the article, I think so...
So thanks a lot for the video version))
Thanks for another great video!
Sorry maybe some here asked the same question, how is the name of the extension you are using to show the error in that big modal in the app instead of going to the console??? please a link to it or the name to search it please?
In all this code/ video there ain't any database-calls / redux used, right?
I wonder, if/how I can use this, if I have to use database calls.. :-S
Really good explanation and short video, thanks!
This is a profound example of by-reference intuition from the real world blowing up on a learning coder. Does Javascript make this any harder, by being subtle about objects vs primitive value ?
Oh, it's an easy trap to fall into for sure. I see "reference types vs value types" and mutation tripping up people all the time. But I wouldn't say objects vs primitives is compounding the issue much. You need to keep track of what you're referencing in any case
Thanks !
Excellent video, just the explanation I needed. Thank you very much for the video
Dear Bro, me install just react version newly but take some problems,, when i use input tag then page show me only "Blank Page" plz help me....
i working last 5 days but no any solution...plz make video not show me blank page..me waiting your video. i am from pakistan
Please also share this code, else it gets very difficult to expirement with it
I encountered exact same problem few days ago 😄.
Thanks a ton for sharing all this valuable info and experience with us. U are a gem for students like me
Emmm if we know, when the current age is increment, cant we then update current user? incerementAge -> ...changing data in users... -> set current user by inceremening with new date
It's dificcult to manage the syncronation, specially when you have lots of states. Keep it simple :)
To go a step further into performance, I'd keep the users state as an object instead, where key is the id and value is the user, so your derived selectedUser state wouldn't have to loop (find) throught the users array every time selectedUserId changed, but actually access it directly e.g. const selectedUser = useMemo(() => users[selectedUserId], [selectedUserId, users]);
Seems like a perfect candidate for a Map.
What's the best to chose among useCallback() and useMemo() ?
When he uses the "users" array to keep track of changes with the useMemo() call....
1. What is react checking exactly behind the scenes? Just the pointer to the array "users" or a more deep comparison (things like length, changing the objects (per se or sub-properties) inside each index, etc) ?
2. Also isn't "users" being re-created on each render cycle? Wouldn't useMemo going to be doing the find() operation every single time?
in other words: if u want referential data, u need to store a reference and not the data
yeah, the example is more about having copies of data, in those cases you need to make sure you update all the copies. It is not really derived data since you can not derive selectedUser from the state before he added selectedUserId to the state.
dudeee.. i'm confusseeeddd.. this is too faaasssttt...
Wouldn't it be more performance to pass users.length into the useMemo dependancy array?
Very important lesson in your software class.. but who in the interpreted react/js world cares about storage? As far as I know it's always important to keep redundancy low, but it's not always the best tradeoff. At some degree of complexity for a specific calculation you should avoid recalculating specific values time over time, rather than just looking them up in the storage. If your use case isn't time-sensitive as it is the case for most basic applications I usually limit this to a depth of 42 additional operations in comparison to a simple lookup in storage, it's ideomatic just for simplicity and to have this as a reminder if time matters 😅 especially in the SQL area with less 'intelligent' DBMS systems - often found in cluster cases - this helps out to improve the timing overall. Most of the time I would recommend using higher structures represented in arrays or maps/objects to keep track of changes more granularly, but that's case specific, as every decision you make in your life.
But oops, watch out for mutants! 👾
Thank you for this content 😭😭😭👍
If users list was huge, isn't it inefficient to filter through them? I would suggest saving the users as object with keys as ids, and then getting the selected user is o(1)
That is also good solution, but then to render users, you have to create array from an Object which would also be O(N) on each render.
Or again we could useMemo on that object and create new array from that object only when users change.
But again, we are down to same thing, either we have create array from users object or that filter derived state.
I guess filter derived state would be faster, as filtering can not always be in worst case, sometimes we can find user ar first place only, and it’s average case would be finding user in mid of an array.
But on other hand we have to create whole array from Object with Object.keys, values or entries function etc.
Which is always exactly O(N).
if the list is huge then the list should get shorter. You can add pagination of some kind
Good argument,. you can have list of ids which can represent the order of rendering, and objects of items in object with id as keys.
Rendering list through the ids array, selecting the object from the objects state.
@@ElektrykFlaaj yeah but when you add pagination and don't have all the data on the UI you lose alot of abilities, like searching on the frontend, and you'll have to do backend search and sort..
@@amitneuhaus2989 yes I guess, depends on use case. If users aren’t going to change much rather selected user would, than using object as initial state in fine, considering we are memoizing the render array with useMemo.
Otherwise, if users data is getting changed often, we can use this array approach shown in the video.
I'm facing a problem in making website responsive....could u plz help me out?...I will ask my doubt if u replied to this message.....
Can't we use Useref() hook to refer to that selected user?
UseMemo would work only if you are creating a new array each time you update a element ... I there is still a optimization there
Excellent!
Nicely done. It would be all too easy to try to useEffect to keep state values in sync, and you dodged that pitfall like a baus.
useMemo also have a cost. cost of useMemo need compare with cost render.
Thanks
Awesome 👍👍
@Kyle The code is still a little too small to read properly on a phone (tho you have the space in x). Thanks for your videos
Nice video, but I have a question, if always change user list, useMemo is worth?, because it added a validation to change its value and always values change, I prefer to acces with index but it not always work
What is the point of useMemo at this case, because if we increment any user age or select new one, there still be .find through list (because there is no other case for rerender), instead we could store key - value object where key is user id and value is user, so we would not need iteration
This is a very simplified example so that it’s easier for beginners to understand. He mentions in the video that this is useful if you have other state or props making the component rerender, like theme for example.
This is jus an example, Real world app would have lot of other states which could cause render.
And using object to store data, with userId as key do look okay on surface.
But it also has same problem.
Cause React cannot render object, you have to create array of users form users object using Object methods like Object.keys, entries etc on every render which would always he O(N) and than map over that array to show users.
Basically we are back to same problem, either use state as array of objects and avoid creating array in every re-render ans use filter to find sleeted item.
Or use object of users with keys being userIds and create array from this object to map over in every re-render
@@anniecenter You if have list with 1000 users and change age to one of them - your array is changing, so it will be .find again through 1000 users even if you did not change selected users and useMemo won't help you with that.
@@arjobansingh1940 You could also store array of IDs, so you will map through array of IDs and get users from key-value storage
@@mikesummer670 again we are storing two states for one data source.. Exactly the problem this video tried to solve.
In real world scenarios, we would have feature of adding, deleting users.
You would have to manage state at both the places with this approach.
But yeah, dependent on use case, if problem statement is not much complex, storing an array for ids differently could be used
bravo!
i used useEffect instead of useMemo is there any downside on this approach ?
Is your course worth it in 2022 react js?
you can select only id, and pick it by id
What is the Jackson model that?
or you can just add a prop to the user you select like selected: true and then you have all your control in only one state :)
You will face another issue in this case. You will need to find currently selected user and set selected to false which in this example is not a big problem, but if you start keeping track of different object states and behaviors inside an object itself it could become a huge pain to update and maintain.
useMemo() doesn't have any use in this component because it has both the state variables in the dependency list! So, whatever we do in this component, it'll run the loop through the whole users list to find the selected user
because it's just an example, in a real application you'll gonna have a lot more states than that
@@brendon205 yeah makes sense 👍
Why not just use the users variable as a dependency in use effect and update selected user in use effect?
Why not simply use useEffect to update selected user with users in dependency array?
because it's not "simply", you're increasing complexity and decreasing readability.
in general if it's not necessary to store something in state then you probably shouldn't store it in state
useEffect will run after render. It is used for side effects.
@@andy-ally constructive
Really great video, valuable to understand. Thanks!
how about using `useRef`?
Why dont make an extra selectedUser field in the users state ,and when u update user age u update age of user on users array and the age of selectedUser the same time.
What if I use useEffect instead?
What's the possible/potential bug here? I don't see any issue with storing the selected user object in state, please provide a concrete example of a potential bug from applying this pattern as opposed to "bad things can happen".
He explained it, when he incremented the age of the user on the list, the age of the selected user wasn't changing, because it was stored as a separate state. It is really a bad practice to store the object in a state, because if you do that, you'll have a duplicate state (the user on the list and the same user on the selectedUser state), and you'd have to update both states manually when some attribute of the user changes (in this case, the age)
Yeah glazed over that at 3 in the morning, agreed, thanks!
Can anyone help me to understand,
After memorizing the function to derive selected user Kyle said that,
It will help to boost performance if there are bunch of other states - which makes sense,
But he also said that if we have 1000s of users it will help with performance,
If users are in dependacy array, meaning that function is going to run and iterate through every user any time user's value change, how would it help here?
without memoizing, every time you click "increment" the 'selectedUser" function would run each time. With useMemo, only when you click "select" the function will run, thus running only when you need it to, and not whenever the component rerenders
@@mateuszwojciechowski495 thanks for the reply, isn't users state in the dependacy array, and on increment aren't we updating users state?
Without useMemo the function runs in each rendering of the component, even if no state was changed.
@@joachimfrank4134 Ohh I get it now, thanks 🙂
Thanks a lot. it seems simple, but not.
iirc the beta.reactjs docs talk about this. Basically never store the same data multiple times in state. Only one copy.
Best solution is selectedUser to be an id or index
For me, the tricky part is hunt down the performance issue.
As an angular developer forced into react, it seems to me that react is prone to bugs. You have to get everything right, every time, or you get bugs and/or poor performance.
when i watch videos like this one, i am glade i chose svelte over react.
I like his eyes
Anyone knows about how to create a javascript sdk ?
November 4, 2019
Great video, one thing I would say about react hook: I just love it! Before I used to use react use state but once I know how hook work, it's just so much better :)
Do you have a Redux course?
2:22, here one way to solve this is: instead of returning a new object in the map, you can do:
if(user.id === id) {
user.age += 1;
}
return user;
this will solve this reference inequality issue. However, you should keep this in mind that you are mutating the object.
Mutating the object will lead to a new bug since the component will not re-render as we are returning the old object Reference.
@@narendrareddyyarramreddy2007
your justification is partially incorrect because that's not how re-render is happening. The array reference is what is being tracked off from state and since .map() returns a new array, this new array and old array is referentially different so react will cause a re-render regardless
@@awekeningbro1207 If I want to return a new object, I would prefer the conventional way of spreading everything and changing required ones.. It looks more cleaner 🤔
@@awekeningbro1207 for this example you are right, array change is causing re-render.
But consider a case, where each row is a new User component which takes user and renders it’s age and that User component is memoized with memo() HOC as well for performance reasons,
as there can be tons of rows, and re-rendering every row just so because some parent’s different state is changed or data of some single row is changed, is not feasible.
Now here, each User component only have user prop, based on its change the User component can re-render.
But now with your approach, each user object is mutated on age change, thus User component will not re-render, causing old age to render on UI.
Yeah, you could say that in current case, that is not the problem, but we should write code in a correct way, which can be easily extended.
Thus we should follow React’s principal of not mutating state.
Instead of writing React’s code in non-recommended way, and then trying to figure out in future why certain thing is not working on extended features or codebase!
Please don't suggest bad practice, that might be working in this case but mutating state is very bad, you will encounter so many bugs doing that
This is the "Single Source of Truth" principle
Doesn't using useMemo like this reintroduce the bug where the age is not incremented for the selected user?
No , useMemo is just used for optimization it has nothing to do with the logic
@@adarshs4871 I mean the number is still incremented, but because of useMemo it will not be shown as incrementing in the UI.
Awesome
is there a way that writing a test would catch this? i dont know tests
You can compare the state and the variables during a test, but it isn't a good thing to test, you should test the funcionalities themselves and how the app is behavioring. For instance in this case the test should be that "if i increment a selected user, both lables should show the same information".
Vue: just use computed
Vue makes this so much simpler
Why?
Why not just store the reference?
And finally third
updating a state using a callback function is also a solution I guess and no need to use extra hooks,
so what do you think about that 🤔🤔
I think using useMemo is overengineering. React developers themselves talk about not doing performance optimizations where there is no bottleneck
Why not just make the user array stateful?
Derived state? It's not tho?
Vue, computed