Skip to content
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

Sub render #830

Merged
merged 5 commits into from
Feb 4, 2018
Merged

Sub render #830

merged 5 commits into from
Feb 4, 2018

Conversation

theKashey
Copy link
Collaborator

This implements idea behind #806 - each Component become a AppContainer.

How - just via pre-render hook it will pre-hot-render tree as AppContainer can do.
Difference - it will do it on Component render, and force-update afterwards. AppContainer is still needed(preferred) to force sync update.

Solves:

  • async components will reconcile them self. No need to mark components as hot - they are all hot
  • solves - a bit of Portals. I am not sure why, but this is not working at 100%. In the example async portal is OK, while sync portal will reset Counter if you will change Counter. And not reset if you will change "hidden counter", which got unmounted in the first case.

Anyway - this feature brings stability, and could solve close to any render quiz, we can get. I am not touching hot-render here, but it could be heavy simplified due to multiple "reconcile" points.

@theKashey theKashey mentioned this pull request Jan 29, 2018
Copy link
Collaborator

@gregberge gregberge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example needs cleaning, I think we need more test on this.

"webpack-dev-server": "^2.9.7"
},
"dependencies": {
"emotion": "^8.0.12",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we do not need emotion in this project.

render() {
return (
<span>
+{this.state.count}:{this.gen++}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Examples should reflect real examples, I think this is not a real example. I suggest to create a simple Portal example.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or an Async Portal but without tricky things.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using them for debug purposes. They reflect only the pure idea of RHL - don't lose the state.
This example is a good one, cos it actually not working for sync portal, as described above.

module: {
rules: [
{
//exclude: /node_modules|packages/, // should work without exclude
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excluding node_modules is a good thing, please add it and remove comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long CRA will not exclude node_modules and some projects might follow the same approach - we shall be ready (and we are ready).

class AsyncComponent extends Component {
constructor(props) {
super(props)
this.state = {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have class properties 😁.

return element
}

setStandInOptions({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely merge projects.

@@ -63,7 +64,7 @@ describe('hot (dev)', () => {
wrapper.unmount()
})

it('should redraw component on HRM', done => {
it.only('should redraw component on HRM', done => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nop

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😨

@codecov-io
Copy link

codecov-io commented Jan 30, 2018

Codecov Report

Merging #830 into next will increase coverage by 1.2%.
The diff coverage is 94.28%.

Impacted file tree graph

@@           Coverage Diff           @@
##           next     #830     +/-   ##
=======================================
+ Coverage    85%   86.21%   +1.2%     
=======================================
  Files        23       25      +2     
  Lines       507      573     +66     
  Branches     96      151     +55     
=======================================
+ Hits        431      494     +63     
- Misses       61       62      +1     
- Partials     15       17      +2
Impacted Files Coverage Δ
packages/react-hot-loader/src/reactHotLoader.js 100% <ø> (ø) ⬆️
packages/react-hot-loader/src/utils.dev.js 100% <ø> (ø) ⬆️
...ackages/react-hot-loader/src/reconciler/proxies.js 100% <100%> (+2.27%) ⬆️
packages/react-hot-loader/src/reconciler/index.js 100% <100%> (ø)
packages/react-hot-loader/src/AppContainer.dev.js 84.21% <100%> (+0.87%) ⬆️
...-hot-loader/src/reconciler/hotReplacementRender.js 87.72% <92%> (+0.54%) ⬆️
...es/react-hot-loader/src/reconciler/proxyAdapter.js 94.44% <93.93%> (ø)
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c2b7c3c...d3e213b. Read the comment docs.

@gregberge gregberge merged commit 4e8e617 into next Feb 4, 2018
@gregberge gregberge deleted the sub-render branch February 4, 2018 21:48
@joshjg
Copy link

joshjg commented Feb 5, 2018

@theKashey

async components will reconcile them self. No need to mark components as hot - they are all hot

Running beta.21, this doesn't seem to be the case. Unless I wrap the async component with hot(module) any change causes it to reload completely.

@theKashey
Copy link
Collaborator Author

theKashey commented Feb 5, 2018

@joshjg - so two questions:

  1. how you detect and actually load async component
  2. please add this code in any place
 import {setConfig} from 'react-hot-loader';
 setConfig({debug: true});

And the check the console output. Something should be inside.

When you hot async component, you setup HRM to component-will-reload-component. It will perform hot-replacement without loader help. So the problem might be with "loader".

@joshjg
Copy link

joshjg commented Feb 5, 2018

  1. Basically the following:
componentDidMount() {
    import(
        /* webpackChunkName: "[request]" */
        `modules/${someDynamicValue}`
    ).then(({ default: Component }) => this.setState({ Component }));
}

And webpack handles the rest.

  1. I don't see any additional output when using that.

@theKashey
Copy link
Collaborator Author

theKashey commented Feb 5, 2018

During hot replacement RHL will not trigger componentDidMount. If it got executed - so something "upper" remounts.
Please double check, the problem is before async component.
Do you have any HMR on top level?

@joshjg
Copy link

joshjg commented Feb 6, 2018

Correct, I have the root component on the top level wrapped with hot(module). Changing something in the async child causes this root to reload (unless I wrap the async child with hot(module) as well).

@theKashey
Copy link
Collaborator Author

That's mean that React-Hot-Loader is not working for something between top-level component and loader.
Can you double check, that changing top-level component does not cause the problem, and next moving deeper try to find the "unmounting point".
I am quite interested in it.

If possible - just share some code.

@joshjg
Copy link

joshjg commented Feb 8, 2018

The loader component is the one that unmounts. None of its ancestors unmount.

@theKashey
Copy link
Collaborator Author

So, @joshjg, could you share some code. There must a reason(or a bug) to unmount.

@joshjg
Copy link

joshjg commented Feb 8, 2018

class ModuleLoader extends React.Component {
    static propTypes = {
        moduleId: PropTypes.string.isRequired,
        moduleName: PropTypes.string.isRequired
    };

    state = { Component: null };

    componentDidMount() {
        this.mounted = true;

        import(
            /* webpackChunkName: "[request]" */
            `modules/${this.props.moduleName}/src`
        ).then(({ default: Component }) => {
            if (this.mounted) {
                this.setState(() => ({ Component }));
            }
        });
    }

    componentWillUnmount() {
        this.mounted = false;
    }

    render() {
        const { Component } = this.state;

        if (Component) {
            return <Component {...this.props}/>;
        }

        return <Placeholder/>;
    }
}

@theKashey
Copy link
Collaborator Author

Normal class 🤷‍♂️
Meanwhile I've found a bug which may lead to unmount, and it actually is #843 , but it is not related to sub-render as everyone thought before, and exists from the first beta.

If you will render one component instead of another - RHL will use the old one.

@joshjg
Copy link

joshjg commented Feb 12, 2018

FYI - beta 22 did not fix this issue

@theKashey
Copy link
Collaborator Author

@joshjg, in this case, I'll ask for some example to reproduce.

@joshjg
Copy link

joshjg commented Feb 13, 2018

@theKashey Couple things

  • I messed up my babel config - this was causing the unmounts. After fixing this, the loader no longer unmounts, but instead the async child never updates.
  • I tried using setConfig({ logLevel: 'debug' }) (instead of debug: true) and found the following error:
React-hot-loader: reconcilation failed due to error TypeError: Cannot read property 'children' of undefined
    at eval (react-hot-loader.development.js:978)

Which is this line: https://github.com/gaearon/react-hot-loader/blob/next/src/reconciler/hotReplacementRender.js#L125

@theKashey
Copy link
Collaborator Author

I've got the idea, easy to fix, but lets double check.
Some render returned an array as a children (it could be nested), but in render-before thats child did not exist.

 { someFlag && [<div>....]

Do you have such code?

@joshjg
Copy link

joshjg commented Feb 13, 2018

Yes, I use that pattern all over.

@theKashey
Copy link
Collaborator Author

Ok, let me write red tests first.

@theKashey theKashey mentioned this pull request Feb 13, 2018
@theKashey theKashey mentioned this pull request Apr 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants