-
Notifications
You must be signed in to change notification settings - Fork 48.3k
API for accessing props.children
correctly
#751
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
Comments
FYI: Internally there's
|
To continue the discussion: personally I'm more for returning the array. Right now it returns the first level of components (i.e. the parent's direct children) and I've found it useful in the past to retrieve the |
+1 for returning the array in each case. A rather insignificant performance optimization should not be the basis for making an inconsistent API. It is just confusing and will byte every beginner user of the library (and even some more expert ones) for no good reason |
I think we should expose |
Wait, so what's the use-case for getting every leaf child? The problem of the current API & getting direct children would still be unsolved if |
In my opinion, exposing a different API to walk the children does not solve the problem. The issue is not that an API to walk the children is missing. As I see it, the problem is that |
Not sure if this throws more confusion into the mix but Om actually sets If people find Side note, as someone new to this issue, this ticket is missing quite a bit of context. Can someone explain what problem this is actually solving? Is it just a matter of convenience for some use cases? |
Yes, Most of the time you don't need any other functionality. However, I think that we need to expose this method in the API -- perhaps on
So I think an optional accessor function is the right way to go, and it's already in the repo: |
@petehunt doesn't Edit: seems like it maps through direct children alright, nvm this. |
@petehunt I don't see how Another side note, I also think that having opaque values on |
Docstrings for Also in terms of memory perfomance I was under impression that |
@petehunt just adding a few more thoughts here related to children & performance. I think it's important for React to support people who want to drive the algorithm from the outside like Om. It's unlikely that React can ever compete with Om since in the Om model we already always know precisely what data changed and already know exactly what should or should not be re-rendered. Because of JS semantics this likely will never be true in the general case for regular React users. We could in fact deliver ridiculous low allocation between renders today if React provided the right hook, i.e. |
@sebmarkbage definitely needs to be on this thread since we used to flatten children to an array (and therefore @swannodette I don't think something like |
@andreaferretti can you describe your use case in a little more detail? |
@petehunt My point is that I do not have a specific use case. My concern is that an API that sometimes returns an array, sometimes a single object, is mislieading, whether or not alternate APIs exist. Even if there are alternate means to get access to the children, some people will use In other words, the probably negligible loss of performance due to an allocation of an array of a single element is more than repaid by the corresponding gain in consistency and uniformity. Why one would need a short article in the docs just to explain that an API does not behave in the obvious manner? |
@swannodette We already consider immutable objects to be a critical part of React. Our future design will continue to revolve around optimizing the reconciler for the immutability. In fact, we're just about to land another optimization that breaks certain mutability use cases. The shouldComponentUpdate hook is currently not doing this by default but it's a critical feature that doesn't work well with mutability. Supporting a mutable JS paradigm for certain components doesn't limit our ability to optimize for immutability for larger sections. In our next release we won't reconcile children that have the same instance as before, by default. We can assume that they haven't changed. The rationale is that if you mutate something on a prop, you also tend to recreate that child. That means that subtree updates that receive children from higher in the tree won't reconcile all the way down. This will work for free if you do subtree updates. Is your issue that you're reconciling from the top and want to avoid rerendering the parents? I'm not sure if I interpret your proposed shouldChildrenUpdate() API correctly. I can think of a few issues. Children are not the only way to pass components. Often you should be passing them through named properties which indicates the intension more clearly. Especially if there are multiple sets of children. There is no special case for children in composite components. You also need to be able to nest children so how do you specify this for a nested set? It also needs to make sense for fragments, i.e. returning multiple components at the top level of a single render function. One of the important ways to do perf optimization is to create smaller components. I.e. if your children tend to update separately from the rest of your component, you should be breaking them apart anyway. Sometimes this means passing them in from above. Sometimes it means wrapping them in another Component. That other component will have shouldComponentUpdate on it so it should never needed to recreate the large list of children. Similarly, if the children are passed through above, and state updates in the current component, they won't reconcile by default. The reason children goes on props is because there's nothing special about the children property. That's just how we express the children property in terms of the XML like syntax in JSX. Since another component can take an opaque set in named property.
If you want to pass a function to lazily instantiate children, that's perfectly valid. You will always be able to pass that explicitly.
The additional arguments to the function just happens to create an opaque set. As for why this set is opaque and why it changes shape... We're aware that this is inconvenient and I tried really really hard to get this addressed before we open-sourced. It turns out to be very tricky to solve the semantics and retain performance. The fact that it's sometimes a single child and sometimes an array is just a perf optimization. Semantically they're equivalent and we could easily just wrap the single child in an array if that was the only issue. The real issue has to do with nested fragments.
We want the ability to conveniently be able to prepend and concatenate sets of components. You can't just flatten one of these to an array, because you would lose the key context. The keys can only be guaranteed to be unique in the set where they were originally passed in. Not in a concatenated set. The initial proposal added a context to the key of a component as it passed along. To get the implementation details to get that right ended up being fairly complicated because you can extract a component from an array and place it in a new place. Ideally it would still preserve it's identity. That worked with the exception that you couldn't place the same component in two places (which you still can't). In the end we reverted the whole thing because of the complexity of the implementation. Another proposal was to make it truly an opaque set where you could never extract values on it. Just operate on it with custom functions. I was against introducing a new set type because we want to be able to interoperate well with a larger JS ecosystem. With ES6 stablizing there are few alternatives that could be possible. There was previously a strongly held believe that arrays must be treated just as any other key/value object. I believe that to be incorrect since in ES6 they can equally be treated as iterable types, which could shift the semantic meaning of an array within one of these fragments. We could potentially expose children as a Map which observably preserves order in ES6. The contextualized keys would be part of the Map's keys. It's also possible to implement this lazily. This is probably good idea to start with. But it still leaves a lot of unknown questions. The same question remains about what you do with an instance that you gain access to from one of these Maps. When you place it in a different position from before, does it retain state? The caller would assume that it would. The final position in the tree may be different. You would also be responsible for preserving the key correctly. We'd probably need the ability to move nodes between parents to preserve the state intensions of the caller. What are you allowed to access on the instance? You shouldn't be calling methods on the instances because they're not stateful and we would even want to prevent that in the future for other reasons. Even if you could call methods on children, you can't rely on a composite having those same methods. That would mean limiting the ability of your parent to wrap parts of it's children into composite. That's a really important design prinicipal. You could possibly want to have the parent inject properties into your children but those may pass through a composite that isn't aware of those. I think there's a larger issue about how a parent communicate with children that it didn't create itself. One way to solve this would be to expose an API that lets the parent resolve children down to an abstraction that it can understand and communicate with using a custom API. We need to figure out what those semantics should be and then we can talk about what the type should look like. |
@sebmarkbage thanks for the long reply clarifies many things with respect to the current design.
This is not how Om works. You only pass data, never components. We never use React subtree updating. Everything is data-driven and all performance is derived from reference equality checks all the way down. This means you can make a composite data structure out of different pieces of data which represent different Om components - re-rendering from the root will still be lightning fast. Thus we don't need (or really want) any of React's high level component semantics for users, we just want to drive the React algorithm. So while
Speaking of which, given that you all already allow users to specify |
Oh and as far as nested children and |
+1 Totally agree with @andreaferretti. |
Eh, I guess... personally I dont feel API like |
Just to clarify, so the idea is that we can still set |
onlyChild should only be used on Components (soon to become Component Descriptors). You can still set the I wouldn't do that with the second argument to the component constructors though (that translates to the children property). I.e. the "children" position in JSX. @swannodette Do you need to use the second argument or can you use an explicit property named |
Currently we are using the 2nd argument to component constructors, however we can easily switch to doing this via |
is |
oh thanks, it has been renamed to |
Sorry, to clarify – onlyChild is the internal name; React.Children.only is the public one. |
Can I traverse them recursively ? This doesn't seem to work
|
Is there a function that returns me all the React children recursively? I need that for collecting and adjusting form data before submit. |
I want to traverse all children recursively, find the specific type of node and use |
(Check the comment on http://facebook.github.io/react/tips/children-props-type.html)
I also feel that saving an array allocation once out of x times causes a bit too much inconvenience. I think we should either return the array all the time or, as @vjeux suggested in IRC, expose an API for getting the children correctly.
The text was updated successfully, but these errors were encountered: