-
Notifications
You must be signed in to change notification settings - Fork 50
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
Support React 0.14 #31
Conversation
We can't reliably retrieve the instances now because React 0.14 pure components don't have them. Instead, it is now the consumer's responsibility to update the rendered tree. We will eventually make this smoother when React exposes an official DevTools API.
To be honest, I don't understand the design decisions of this pull request. Here are some thoughts: Pure components(/functions) are pure: they don't need to be proxied. If I've understood right, the original idea behind the proxies was to preserve the internal state of React components across the component versions. But because the "pure components" don't have any internal state, there is (in my opinion) no need to create proxies for them - when the function implementation changes, the next re-render will apply the changes. In addition, because pure components are just functions, detecting them programmatically (e.g. by using react-transform) is practically almost impossible. Or do you have any idea how to tell if the following one is a component or just a helper/utility function (okay, perhaps I little bit artificial example but I hope you understand the point)? export default function() {
const [val, defaultVal] = Array.prototype.slice.call(arguments)
return val ? <h1>[val}<h1> : <h2>{defaultVal}</h2>
} And finally, I don't see the removal of
Do these thoughts make any sense? |
I've been thinking about this for a month so believe me I did not make this change light hearted. 😉
If they're not proxied, this means we let their identities change, and let the changes bubble.
I think the third point shows that “functions are hard to detect” is really isn't an argument in favor of doing nothing because, if we give up on detecting them, we might as well never But then if we have to always bubble hot updates up, any edit deep inside the tree will cause the whole tree to lose DOM and state just because there was a pure component somewhere along the way. So we get back to where we started. Finally, there is module level error handling. If there's a module level error, Webpack is simply going to abort hot update (and any future hot update) if we don't handle it inside the module. Accepting hot update via But to answer your question, yes, they are hard to detect, but we don't have to handle every case. We only need to handle the most common patterns, and document that.
Any pure function that returns a ReactElement, null, or false, is a component now. So technically yes, it is a component because it can be used like one. And nothing bad happens if React Proxy treats it like component. Of course this is pushing it too far: for example,
I hope I showed above why this workflow, as intuitive as it seems, does not achieve the goals of these projects (keeping state and DOM and doing our best to recover from errors). Returning an array of nodes gives the false idea that we support this tracking fully when it isn't true, and assumes that the consumer has some sort of bubbling mechanism available to them (which also often isn't true). Asking the consumer to register the root instance node themselves isn't a huge deal, and is much more reliable. In the future React will provide a special official reflection API for things like DevTools, which we'll use to grab the root instances—that's when we'll be able to remove this restriction, but not now. Finally, we should all understand that React Proxy, React Transform, etc, are all temporary solutions. We are now experimenting with hot reloading and figuring out what on the React side makes it hard. There are some obvious pain points: state lives on instances (facebook/react#4594), no error boundaries (facebook/react#2461), no DevTools API (facebook/react#4593), etc. As these get solved, I hope that eventually we'll be able to move to a more natural approach like you described where hot updates “just bubble” and it still works. I know @sebmarkbage hates all the dirty business I'm doing. 😛 But I don't want to compromise on user experience, so I'm only ready to jump to a more “simple” solution when it's also a better one, and ideally officially supported on the React side, which I hope to work on in the future. |
By the way I have high hopes for module pattern: When React supports something of the sort, we won't have to worry about instances. Even preserving the identity will be much easier because module is just an object. Nothing like the function-proxying and prototype-patching business we've got going on right now. So I'm hopeful for the future. |
Regarding stateless component detection by inspecting AST there's a PR to react-docgen which might be useful here. |
@gaearon Just to make sure I understand -- you're saying that you are intending to support stateless components with react-proxy, react-transform and react-transform-hmr? I ask because about 90% of my app could be stateless components, but I won't bother migrating if it's going to mean there won't be support for react-transform-hmr. |
Well spotted, missed this one since I personally don't mix stateful and pure components in my sub-trees. I agree that this sub-tree re-rendering might be an issue.
We should not rely on that even at the moment (if I understood your statement right). We should always examine the exports because if we always accept the file that contains at least one component, we might miss references that are meaningful to our business logic: // someUtils.js
// this one causes the module being accepted every time when the file changes?
class SomeUtilComponent extends React.Component {
...
}
export function renderUtils() {
return isUtilEnabled() ? <SomeUtilComponent /> : <div>bal</div>
}
export function isUtilEnabled() {
// how about if we make changes to this function?
return !isIE()
} If the pure components become mainstream, mixing utility code with components is likely to become more common. Then it's crucial to reload the references to "non-component" functions. Perhaps I just missed your point and you were meaning exactly same but just to make sure.
I'm sorry I formulated my point unclearly. I agree with this once - something being hard to implement is not an excuse to not to implement it. My point is that if the pure component detection and proxying is applied, it must not cause false positives. Or at least if any false positives occur, the proxy implementation must not break the original behaviour. Please bear me if I misread the current implementation but the following code would break the original implementation if the rule above is applied with the current implementation that loses function arity information (this is some code I could write, and I'd be quite pissed to change my implementation just for the sake of reloading) // form.js
function Form(submitAction, props) {
return ( /* ...render form... */ )
}
export default curry(Form)
// somewhereInApp.js
import From from "./form"
import {submitUserProfile} from "./actions"
const UserProfileForm = Form(submitUserProfile)
function MyComponent({userProfile}) {
return (<some-jsx>...<UserProfileForm {...userProfile} />...</some-jsx>)
}
I have to disagree with this one. As a reloading tool developers, we've failed if we need to ask the developer to add some extra code to the production codebase just to make reloading work. The more serious thing is that we have to expose The same "complain" (:smile:) partially applies to the existence of separate
I'm not familiar with internal error handling of Webpack HMR so I can't comment much about this one. 😄 But even now, there are cases when |
Seems totally horribe and complex, especially the "declarative" component module example. I'll switch to P.S. P.P.S. Of course wonderful to HMR. But still the actual development aspect... Personally preferring a little bit poorer HMR capabilities over complex and harder-to-(read|understand) codebase. |
@JeremyRylan, correct. At the moment there are no working HMR implementations that support |
@JeremyRylan
Sure, I intend to support them. |
I'm already asking people to do this for Redux DevTools so I don't really see the problem. React also explicitly tells people to envify their code to get the production version. What I have in mind is this: const instance = render(<App />, rootEl);
if (process.env.NODE_ENV !== 'production') {
require('react-transform-hmr').addRootInstance(instance);
} I don't see this as a big deal at all, and anyway, it's a temporary solution until React gets official DevTools API. |
I absolutely agree with this.
You're not supposed to use it directly. It's supposed to be a depedency of higher level tools like LiveReactload or |
No need to attack straw man. These are “proposals” in an “experimental” repo precisely because this is what they are. Obviously if they were finished and sensible they wouldn't be there, and would be on track for implementation instead. 😉 But that's irrelevant to this thread anyway. |
It depends on the "weaver" implementation. If it replaces function declarations with constants then the evaluation order might be an issue: // before
export default compose(doThis, doThat)
function doThis(args) {
...
}
function doThat(args) {
...
} // after
export default compose(doThis, doThat)
const doThis = proxy(function doThis(args) { ... })
const doThat = proxy(function doThat(args) { ... }) |
Good point. I think we should only wrap top-level |
No description provided.