Generating random/unique attributes server-side that don't break client-side mounting
See original GitHub issueConsider, for example, a relatively generic <Field />
component:
var fieldCounter = 0;
var Field = React.createClass({
getInitialState: function() {
return { inputId: 'field-' + ++fieldCounter };
},
render: function() {
var props = this.props,
inputId = this.state.inputId;
return (
<div className='field'>
<label htmlFor={inputId}>{props.label}</label>
<input type={props.type} id={inputId} name={props.name} value='' />
</div>
);
}
});
In order for the <label />
tag to be semantically related to the <input />
tag, we obviously need to match the for
attribute to the <input />
tag’s id
attribute. Outside of that requirement, however, we have no need of the id
attribute, so a solution like the one above is convenient as it handles that all internally, with no need for a consuming component to pass in an actual id
value.
The problem I’m running into, however, is that this causes the id
attributes generated client-side to mismatch what was sent down by the server (the client-side fieldCounter
restarts at 0
on every load, whereas the server-side reference obviously just keeps growing).
This mismatch then results in an error being thrown:
Invariant Violation: You're trying to render a component to the document using server rendering [...]
So, my question is this: am I overlooking an obvious solution here? I would like for the <Field />
component to be able to simply internalize the id
generation as an implementation detail, but I can’t seem to come up with a good mechanism for then matching up that id generation client-side.
Thanks in advance!
Issue Analytics
- State:
- Created 8 years ago
- Reactions:1
- Comments:17 (15 by maintainers)
Those are interesting discussions, but I’m curious exactly what you mean by “requiring all IO go through React”, what kinds of IO is it referring to? (Or is it in the sense that no data should exist outside of components, like Flux stores does? So all data can be serialized and is guaranteed to represent the entire state?)
…
My replies below are purely gut feeling, intuition and brainstorming 😃
The way I see it, if you give me (per-root) Time, a Seed, a UUID and a way to store per-root local data. With that a component should be able to accomplish any useful deterministic process without requiring any additional data be sent from client to server.
UUID is only necessary for producing markup with GUIDs, which seems like a necessity only because of HTML deficiencies and should not otherwise be useful I think… but it might have other uses.
I think the key point of these features is there is no benefit in having more that one definition/source of each, it’s probably only detrimental even. But these are values that have to be transferred from server to client, if React does not provide these then components has to make these demands on other libraries or on the consumer of the component, neither of which seems at all beneficial (because it’s redundant, leads to duplication of data and code, inconsistencies, etc).
But Time, Seed and UUID have fairly limited usefulness to reusable components and when those are needed they could be provided on an as-needed basis by the consumer if we want to be hands-off (but UUID is intrinsically useful to some reusable ReactDOM components). So I think the primary issue is that per-root local data simply doesn’t exist currently, because even if you have Seed you’re unable to use it for anything stateful because you have nowhere appropriate to store the data without putting explicit requirements on the consumer for making specific per-root data storage available through context (which is a slippery slope from a maintenance perspective).
So yeah it seems to me that (technically) it is indeed a solution to the above lack of “root local data”, which is the primary problem. 👍
What to do about Time, Seed and UUID is a secondary problem. We may decide not to provide some/all in core, but I think it would be a mistake not to at least make them available as separate “preferred” modules (possibly even just as interfaces). So that reusable components can specify them as dependencies. But again, less of an immediate concern.
PS. I think
clock
must be a function, it has to be on-demand.Mostly brainstorming here, so I’d be curious to hear what the rest of the team thinks (and nothing I say here constitutes an official recommendation). Personally, I would go with option number 2.
What you’re really looking for is a UUID, but you don’t want to use random because then the markup wouldn’t match 😕.
Ultimately, this is the server-side-clock problem (ie. suppose you had a component that rendered the current time displaying microsecond accuracy; the two machines are going to have clock skew, making it impossible to generate matching markup). Incidentally, since
rand()
reads the clock, it’s literally the same problem 😛.When framing the problem as the server-side-clock problem, I think it’s pretty clear that you want to have some way of controlling the current time (perhaps you want the clock to be red&green on christmas, and have eggs&bunnies on easter) because you need a way to test this functionality more than once per year. To write unit tests to verify the functionality, you would need to control the time.
The difference is that, for your component, your need for a unique identifier is an implementation detail not visible to the user, so the dependency on sideways data kinda sucks. Eeh, not sure what to do about that.
In the near future, you will be able to get around the cumbersome passing-down-props problem by using
context
(actually, you could use it today, but it’s officially undocumented and unsupported and subject to change).In the more distant future, you will be able to use sideways data loading, perhaps in combination with context, to subscribe to external data like the clock.
@jmar777 does that help a little? @sebmarkbage thoughts?