-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
React class to functional conversion - final #3231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Release EnvironmentsThis Environment is provided by Release, learn more! 🔧Environment Status : https://app.release.com/public/Processing%20Foundation/env-1672cfe859 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not done reviewing yet but figured i should submit the first wave of comments so you can get started!
} | ||
}, [renameValue, sketch.id, changeProjectName]); | ||
|
||
const handleRenameChange = (e) => setRenameValue(e.target.value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so i was looking into useCallback since that function is new to me and I read in the docs that it's typically used when functions are passed as props to prevent unnecessary rerenders -- I was wondering if we want to apply it to all of these functions that are being passed as onClick props? Or what's the logic for only using it for a few of them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah @khanniie some of them don't have to be rendered using useCallback since:
- When to use useCallback:
- Passing callbacks to child components: If a callback is passed as a prop to a child component and that child uses React.memo to prevent re-renders, using useCallback ensures that the callback doesn't get recreated on each parent re-render, preventing unnecessary child re-renders.
- Expensive computations: If the function involves expensive computations (e.g., heavy calculations or side effects), wrapping it in useCallback can avoid recreating the function unnecessarily.
- Don't have to use useCallback:
- No performance gain: For simple state updates or inline event handlers (e.g., handleChange, handleClick), memoization won't provide much benefit and adds unnecessary complexity.
- Not passed as props or dependencies: If the function isn’t passed as a prop or used in a useEffect dependency array, memoizing it is unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much for working on this! It's really awesome that all of these files are getting revamped!
I'm doing some quick manual testing here, and I couldn't find any major issues in most of the changed components except for FileNode.jsx
, which seems to cause some crashes when opening certain example sketches (i.e 3D: shader as a texture). I wanted to also start pointing this out now, but will continue to review more closely!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just took a closer look through some of the files! Added a few more thoughts, let me know if you have any questions about any of them, thanks!
client/modules/IDE/components/CollectionList/CollectionList.jsx
Outdated
Show resolved
Hide resolved
@@ -227,6 +227,10 @@ const Console = () => { | |||
}; | |||
}); | |||
|
|||
useEffect(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering if you intended on keeping this for this PR!
} | ||
useEffect(() => { | ||
dispatch(AssetActions.getAssets()); | ||
}, [dispatch]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i feel like since this is from the constructor we probably don't need anything in the dependency array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dispatch
never changes its value so it doesn't make a difference whether it's in the dependency array or not. The effect will run one time on mount either way. Personally I'm in the habit of including it to appease linting rules such as react-hooks/exhaustive-deps
. But we don't have that rule enabled on this repo (at least not yet) because we violate it all the time.
getCollections(propsUsername || user.username); | ||
resetSorting(); | ||
}, [ | ||
projectId, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here, since its from the constructor do we need the dependency array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the value of propsUsername
were to change then we would want to redo the fetch with the new username. So you need that as a dependency.
If the projectId
were to change then you would want to redo the project fetch, but not the collection fetch. So I would have multiple useEffect
hooks with separate dependencies.
const username = propsUsername || user.username;
useEffect(() => {
if (projectId) {
getProject(projectId);
}
}, [projectId]);
useEffect(() => {
getCollections(username);
}, [username]);
useEffect(() => {
resetSorting();
}, []);
I'm not sure what you mean by "from the constructor"? In practice, with the way that the app is set up, the value of propsUsername
probably doesn't change without a total unmount and remount because we never link from one user profile to another. Maybe if you've viewing another user's collection page and you click the collections link from your user dropdown? That's the sort of situation where there could be bugs if we don't have the right dependencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you nahee!!!!
Thanks so much for your work on this @nahbee10! I think maybe one additional change that would be great is to remove the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
getCollections(propsUsername || user.username); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like the username
should be required and it's a mistake that we don't have it marked as such. I'm basing that off of two observations:
- The current code does not have any sort of fallback for if the username is not provided in the
getCollections()
call. - The current condition in
getTitle
for checking if this is your own collection or someone else's isif (this.props.username === this.props.user.username) {
. So that definitely implies that when viewing your own collection we would expect thatusername
prop is explicitly provided and that it matches the username of the current logged-in user.
const { username, assetList, loading } = useSelector((state) => ({ | ||
username: state.user.username, | ||
assetList: state.assets.list, | ||
loading: state.loading | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a big deal but FYI it's better to have separate useSelector
calls for each piece of state that you read:
Call useSelector Multiple Times in Function Components
When retrieving data using the useSelector hook, prefer calling useSelector many times and retrieving smaller amounts of data, instead of having a single larger useSelector call that returns multiple results in an object. Unlike mapState, useSelector is not required to return an object, and having selectors read smaller values means it is less likely that a given state change will cause this component to render.
However, try to find an appropriate balance of granularity. If a single component does need all fields in a slice of the state , just write one useSelector that returns that whole slice instead of separate selectors for each individual field.
https://redux.js.org/style-guide/#call-useselector-multiple-times-in-function-components
Fixes #2709
Changes:
applied react functional component conversion to CollectionList, SketchList, FileNode, ConsoleInput, AssetList. This would be the last conversion ticket
I have verified that this pull request:
npm run lint
)npm run test
)develop
branch.Fixes #123