-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
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] Add uniqueId generator #4253
Conversation
function getUniqueIdGenerator() { | ||
let index = 0; | ||
return () => { | ||
return `mui-id-${index++}`; |
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 don't think that it would work. Have a look at #3533.
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.
TL;DR The index increase faster on the server-side than on the client-side.
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 function returns a generator function instead of a uniqueId directly.
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 This id generator is created per context, i.e. per request. If the client and server call the getUniqueId function in the same order then this method is deterministic. And since every request will have it's own context no two requests will have reference to the same index
variable captured in that function.
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.
Oh sorry, I have overlooked the context part 👍. I though we were going back to the previous implementation.
|
||
describe('one generator', () => { | ||
it('should generate different uniqueIds each time it is invoked', () => { | ||
assert.notEqual(gen(), gen()); |
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 would rather use notStrictEqual
.
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 The generated ids are used as html tag attributes in most cases, which may contain an implicit type conversion.
const id1 = 1;
const id2 = '1';
document.head.id = id1;
document.body.id = id2;
console.log(id1 === id2); // false
console.log(document.head.id === document.body.id); // true
id1
and id2
will pass notStrictEqual
. But document.head
and document.body
will have conflicts using same id.
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.
Should we call toString
and return a string to prevent this implicit type conversion?
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 I agree with your previous opinion. 😲Using notEqual
or toString
here focus too much on the the generator's use cases, not its own functionality. I should add some tests to TextField
.
@AJIH Could you have a look at my comment, squash down the commit and use the PR title as the commit messages? We will be good to go 🚀. |
@oliviertassinari Done! 😁 |
I'm guessing the name was chosen to be in keeping with |
@mbrookes It may be ambiguous to name the module |
@mbrookes it's what it does, hence the name. I think it's more clear this way. it dose give you a new uniqueIdGenerator whenever you call it, it's a factory. 🏭 🏭 |
Got it, thanks. |
Docs site doesn't use
|
😱 😱 That's not good. should we put it on the muiTheme or should we patch docs? a context for one single component kinda seems overkill. maybe putting it on muiTheme some how isn't all that bad? But this is the best approach for the issue though. we have to either patch docs or put it on muiTheme. |
@alitaheri putting it on |
Should we just rename |
😲Doc site has a |
@alitaheri 😱I found another bug of current implementation. Example code(https://jsfiddle.net/aji_h/6o63e7gL/4/): class MuiThemeProvider extends React.Component {
...
getChildContext() {
return {
muiTheme: this.props.muiTheme || getMuiTheme(),
uniqueIdGen: this.props.uniqueIdGen || getUniqueIdGenerator(),
};
}
}
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {clicked: false};
}
onClick = () => {
this.setState({
clicked: !this.state.clicked
});
};
render() {
return (
<div>
<button onClick={this.onClick}>Click Me</button>
<MuiThemeProvider>{
this.state.clicked ?
<div><TextField /><TextField /></div>
: <div><TextField /></div>
}</MuiThemeProvider>
</div>
);
}
} After clicking the button, the first |
I have refactored the code to fix the bug above mentioned and added a |
Wow this is nice 👍 I think it's ready, @callemall/material-ui Take a look please 😁 |
@alitaheri My pleasure. |
``` | ||
|
||
Read more details about uniqueId in the [issue](https://github.com/callemall/material-ui/issues/3757) and the [PR](https://github.com/callemall/material-ui/pull/4253). | ||
|
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 sure the PR thread adds much value - it's mostly comments like this one. 😄
Some comments on
Simplest to most advanced, and coincidentally alphabetical order. 😄 |
}; | ||
|
||
state = { | ||
uniqueIdGen: this.props.uniqueIdGen || getUniqueIdGenerator(), |
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.
Any reason for using the state here? Why not following the muiTheme
pattern?
According to the React docs site:
The getChildContext function will be called when the state or props changes.
I see one potential issue, the uniqueIdGen
context won't be updated according to the property.
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 I tried using the muiTheme
pattern and found a bug described in #4253 (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.
That looks like the expected behavior. What about this version?
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.
In your version, components in MuiThemeProvider
will invoke all lifecycle methods(including componentWillMount
, where TextField
get its uniqueId
) each time the button is clicked because the virtual DOM trees are totally different.
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 You mean MuiThemeProvider
should return a new uniqueIdGen
whenever its getChildContext
is called? I think it should keep uniqueIdGen
all its lifecycle.
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.
Yes indeed. getChildContext
is called at each rerender. So your point is that getUniqueIdGenerator()
should only be called at the mount time. That's sounds good 👍.
But, shouldn't we update the uniqueIdGen
state during the componentWillReceiveProps
hook?
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.
Updating the uniqueIdGen
may cause some issues. Mounted components will keep their ids mui-id-0
, mui-id-1
...But if there are components will mount, their uniqueIdGen
may start at 0
, and their ids would be mui-id-0
, mui-id-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.
I see, let's not update it in this case. It looks like we have reached the limitation of this solution.
I can't see real use cases for this edge case, I think that we are fine.
Should we add a comment?
P.S. I start to feel like using an heuristic for the id generation would have been simpler. 😁
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.😂 In fact, the feature has been discussed a bit before in facebook/react#1137 and facebook/react#4000. We use many hacky methods in this solution because we need a global counter which should reset itself each time React calls render method. But Material-ui is a component library and should not inject the render process.
If MuiThemeProvider
is the root provider, many potential issues would be eliminated. But in doc it's only preferable, not necessary.
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 How about using a global uniqueIdGen
(like your previous implementation) and adding an API to allow manually reset uniqueIdGenerator
before rendering?
The more I think about this PR the more I think we are over-engineering the solution. |
@oliviertassinari Manually setting |
@alitaheri Do you still feel that way? Both solutions came with their tradeoffs, but I think that the |
@oliviertassinari I kinda feel the same. In my opinion both solutions seem a bit hacky. I'd say we should think outside the box a little. This id is used for 2 purposes: 1. aria 2. focus If we drop the mandatory aria support ( users should provide id of their own to enable this ) and solve the focus problem in a different way, then this won't be a problem. In other words, the only reason we need this is the focus. How about handling this with js? handle the touchTap on label and call focus on textarea? That should solve it right? |
This PR never sat super well with me but everyone else seemed happy so I let sleeping dogs lay. It did seem like we were over-engineering and putting a very specific function on Do you think we should re-use your last PR? |
@nathanmarks I like the idea proposed by @alitaheri. We could be using the |
@oliviertassinari I would hold on implementing anything dependent on |
53b828e
to
ea2538e
Compare
Fix #3757.