-
-
Notifications
You must be signed in to change notification settings - Fork 32.5k
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
[core] Use context to pass data between Group components and their children #14585
Conversation
46f5689
to
3436e54
Compare
Have you tried rethinking this whole approach with hooks in mind? I'd imagine something like a |
eac964a
to
daf490d
Compare
@material-ui/core: parsed: +0.15% , gzip: +0.23% Details of bundle changes.Comparing: a5b8011...ec23622
|
c4a0ede
to
0cae737
Compare
@eps1lon Hooks have already been thought about 🎉 I’ve added a demo showcasing them and changed the name Note: If we agree on the name change withSelectableContext should probably be changed too. |
d53bd28
to
d113351
Compare
packages/material-ui-lab/src/SelectableGroup/SelectableGroup.js
Outdated
Show resolved
Hide resolved
Can we try to use the same module with the RadioGroup component? |
It’s on the todo list ☝️😉 |
eac851c
to
d9a3e80
Compare
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'm happy to see the migration of the tests to the mount API :)
packages/material-ui-lab/src/SelectableGroup/SelectableGroup.js
Outdated
Show resolved
Hide resolved
packages/material-ui-lab/src/SelectableGroup/SelectableGroup.js
Outdated
Show resolved
Hide resolved
packages/material-ui-lab/src/SelectableGroup/SelectableGroup.js
Outdated
Show resolved
Hide resolved
eb4f195
to
23bea2d
Compare
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.
This is proper programming. How I love react these days: using patterns, functional programming for complex behavior, hiding implementation details. Makes the code so much cleaner, easier to navigate and potential optimization emerge by themselves.
Just some minor things left to discuss.
@@ -7,6 +7,7 @@ import withStyles from '@material-ui/core/styles/withStyles'; | |||
import { fade } from '@material-ui/core/styles/colorManipulator'; | |||
import ButtonBase from '@material-ui/core/ButtonBase'; | |||
import withForwardedRef from '@material-ui/core/utils/withForwardedRef'; | |||
import useSelectedState from '@material-ui/core/internal/SelectableGroup/useSelectedState'; |
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's not really internal then. I don't mind having this as a top-level export and do
import useSelectedState from '@material-ui/core/internal/SelectableGroup/useSelectedState'; | |
import { useSelectedState } from '@material-ui/core/SelectableGroup'; |
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.
@oliviertassinari What do you think? (context: #14585 (comment))
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.
@eps1lon I don't follow. I don't see anything making this module public. Why should it be made public? What do you think of keeping it private, at least, until it proves its purposes?
|
||
it('should not be called if the click is prevented', () => { | ||
const handleChange = spy(); | ||
describe('non exclusive', () => { |
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 think that test is better placed at SelectableGroup
.
); | ||
}); | ||
|
||
describe('imperative focus()', () => { |
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.
Pretty much going on already in this PR. As a PoC this is good enough.
I'd say we
- don't apply to RadioGroup yet
- Convert RadioGroup to function component
- add the imperative focus handle
- use SelectableGroup
Point 2-4 can be combined in a single PR but should each be done in its own commit. Converting to fuction component creates a lot of diff noise.
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.
Sounds like a plan 👍
packages/material-ui/src/internal/SelectableGroup/SelectableGroup.js
Outdated
Show resolved
Hide resolved
packages/material-ui/src/internal/SelectableGroup/SelectableGroup.js
Outdated
Show resolved
Hide resolved
packages/material-ui/src/internal/SelectableGroup/SelectableGroup.js
Outdated
Show resolved
Hide resolved
packages/material-ui/src/internal/SelectableGroup/withSelectableContext.js
Outdated
Show resolved
Hide resolved
packages/material-ui/src/internal/SelectableGroup/SelectableGroupContext.js
Outdated
Show resolved
Hide resolved
packages/material-ui/src/internal/SelectableGroup/SelectableGroup.d.ts
Outdated
Show resolved
Hide resolved
Co-Authored-By: joshwooding <12938082+joshwooding@users.noreply.github.com>
… into selectable-group
@@ -48,6 +48,7 @@ | |||
"hoist-non-react-statics": "^3.2.1", | |||
"is-plain-object": "^2.0.4", | |||
"normalize-scroll-left": "^0.1.2", | |||
"object-is": "^1.0.1", |
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.
Do we need a dependency for such a small module?
https://github.com/ljharb/object-is/blob/master/index.js
@@ -0,0 +1,104 @@ | |||
import React from 'react'; | |||
import PropTypes from 'prop-types'; | |||
import * as utils from './utils'; |
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.
What do you think of inlining the helper in this file? I'm not sure to see what we win at creating a single dependent module of 32 lines.
} | ||
|
||
// Only call onChange if it exists and state has changed (Object.is mimics React) | ||
if (action.onChange && !is(newState.selected, state.selected)) { |
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.
What's preventing us from ignoring NaN and use ===
over object-js?
@@ -7,6 +7,7 @@ import withStyles from '@material-ui/core/styles/withStyles'; | |||
import { fade } from '@material-ui/core/styles/colorManipulator'; | |||
import ButtonBase from '@material-ui/core/ButtonBase'; | |||
import withForwardedRef from '@material-ui/core/utils/withForwardedRef'; | |||
import useSelectedState from '@material-ui/core/internal/SelectableGroup/useSelectedState'; |
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.
@eps1lon I don't follow. I don't see anything making this module public. Why should it be made public? What do you think of keeping it private, at least, until it proves its purposes?
/** | ||
* @ignore - internal component. | ||
*/ | ||
function useSelectedState() { |
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.
We can use React.useContext(SelectableGroupContext)
directly in the components. Why abstracting it?
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's not abstraction it's readability while hiding implementation details. Just because we can doesn't mean we should.
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.
readability, I haven't noticed the difference. My point of interest is the potential module overhead removal by overhead, I mean from a maintainer perspective: "we have x dependencies, what each dependency is doing? vs I have x - 1."
Now, I have a co-worker that "loves" (find it easier to follow) creating modules for 1 line functions. I understand the motivation.
|
||
return ( | ||
return ( | ||
<SelectableGroup onChange={handleChange} value={value} name={name} exclusive> |
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.
As we are using the exclusive
mode. Can't we save SelectableGroup bundle size by using a context forwarding direct? I mean, I would anticipate a straightforward logic without SelectableGroup, but by using the context. Do we need this abstraction? Maybe it's only suited for the ToggleButton? I was thinking of doing something similar with the Tabs component. I'm not I guess React context is all we need?
I'm happy to see the class -> function component migration and shallow -> mount :) |
@joshwooding I'm having a existancial question, I'm looking at https://github.com/palmerhq/radio-group/blob/master/src/index.tsx codebase, using the context seems straightforward. I'm wondering if we need a SelectableGroup abstraction. I mean, let's compare option A and B. |
That has no exclusive/non-exclusive option, no uncheck action. This is why you abstract it. Not because of DRY but because there is separate behavior that is shared among different components. Why is it so important for you to use the context directly here? It would be nice to have a concrete discussion about this and not just esoteric questions like "why not do X instead of Y?" It's your task to compare these different options not just point to X and say it's better and leave use to debunk this. That's not how a healthy discussion works. I'm really confused why you think the code you linked is a valid comparison here? We're trying to solve this issue for ToggleButtons, RadioButtons and in the the Select components as well. That solution has only a fraction of this scope. |
@eps1lon People are reading our code base, often as a reference, people are also reporting that we overengineer stuff. The fewer layers of abstraction of code we can get away with, the simpler. So, I'm challenging the need for it, it's a potential simplification opportunity. I'm only interested in the pros & cons.
I believe there we are doing multiple things at the same time.
I was the first one to encourage the usage of SelectableGroup for the RadioGroup. But, I have more context around the topic now. So I'm challenging it. |
@joshwooding @eps1lon I agree. What do you think of splitting the effort into 3 different steps? 1. hook, 2. context, 3. SelectableGroup. One pull request at the time. |
I'm going to close this for now |
Continuation of #12961
Todo