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

Conditionally Import Components? #2375

Closed
richierob62 opened this issue May 26, 2017 · 7 comments
Closed

Conditionally Import Components? #2375

richierob62 opened this issue May 26, 2017 · 7 comments

Comments

@richierob62
Copy link

Is there a way to conditionally (and dynamically) import the modules needed without ejecting my create-react-app project?

For example, in the code below, in any one render of DetailsField, only one of the 6 imports will ever be needed.

import React from 'react'
import styled from 'styled-components'

import TextInput from './text_input'
import SelectInput from './select_input'
import TypeaheadInput from './typeahead_input'
import DateInput from './date_input'
import RadioInput from './radio_input'
import CheckboxInput from './checkbox_input'

const FieldWrapper = styled.div`
    flex: 1;
    margin-top: 9px;
    margin-bottom: 3px;
`

const DetailsField = (props) => {
    const {
        field_definition,
        current_record,
        action_word,
        mode,
        dispatch,
    } = props

    const label = field_definition.get('label')
    const input_type = field_definition.get('input_type')
    const ref_table = field_definition.get('ref_table')
    const readonly = field_definition.get('readonly')
    const display = readonly || mode === 'display'

    const relevant_component = () => {
        switch (input_type) {
            case 'text':
                return (
                    <TextInput
                        label={label}
                        record={current_record}
                        action_word={action_word}
                        dispatch={dispatch}
                        display={display}
                    />
                )
            case 'select':
                return (
                    <SelectInput
                        label={label}
                        record={current_record}
                        action_word={action_word}
                        dispatch={dispatch}
                        ref_table={ref_table}
                        display={display}
                    />
                )
            case 'typeahead':
                return (
                    <TypeaheadInput
                        label={label}
                        record={current_record}
                        action_word={action_word}
                        dispatch={dispatch}
                        ref_table={ref_table}
                        display={display}
                    />
                )
            case 'date':
                return (
                    <DateInput
                        label={label}
                        record={current_record}
                        action_word={action_word}
                        dispatch={dispatch}
                        display={display}
                    />
                )
            case 'radio':
                return (
                    <RadioInput
                        label={label}
                        record={current_record}
                        action_word={action_word}
                        dispatch={dispatch}
                        display={display}
                    />
                )
            case 'checkbox':
                return (
                    <CheckboxInput
                        label={label}
                        record={current_record}
                        action_word={action_word}
                        dispatch={dispatch}
                        display={display}
                    />
                )

        }
    }

    return (
        <FieldWrapper>
            {relevant_component()}
        </FieldWrapper>
    )
}

export default DetailsField

@gaearon
Copy link
Contributor

gaearon commented May 26, 2017

What problem are you trying to solve? Even though “in any one render of DetailsField, only one of the 6 imports will ever be needed”, this doesn’t affect how bundles are created.

Imports need to be resolved statically, so declaring them at the top is necessary.

@gaearon
Copy link
Contributor

gaearon commented May 26, 2017

To be more specific, I think there might be a misconception of how imports work, and I want to ensure we’re on the same page. Even if there was some way to import just one “dynamically“ it would not really make sense from the bundling perspective. If code is bundled statically, all files get packaged anyway.

You could use a dynamic import() and load chunks on demand, but then you wouldn’t be able to do it synchronously (as your code implies).

So it’s not a limitation of the tool, or something you’d eject for—it’s fundamentally how modules work with JS bundlers. Hope this helps! Let me know if it’s still not very clear.

@gaearon gaearon closed this as completed May 26, 2017
@richierob62
Copy link
Author

richierob62 commented May 26, 2017

Yeah, I’m really unclear on some of the “magic” under the hood of bundling.

I get that, if everything is statically bundled, then DetailsField “includes” its defined dependencies, whether or not they are used at runtime. I guess I’m thinking that if I render DetailsField 1000 times, then I’ll have 5 * 1000 = 5000 unnecessary modules imported that are taking up space in memory.

You could use a dynamic import() and load chunks on demand, but then you wouldn’t be able to do it synchronously (as your code implies).

Apart from my linter complaining, could I do this then? (I'll give it a try)

const relevant_component = () => {
    switch (input_type) {
        case 'text':
            import('./text_input')
            .then(({ TextInput }) => {
                return (
                    <TextInput
                        label={label}
                        record={current_record}
                        action_word={action_word}
                        dispatch={dispatch}
                        display={display}
                    />
                )
            })
            .catch(err => {
                // Handle failure
            })
        case 'select':
            import('./select_input')
            .then(({ SelectInput }) => {
                return (
                    <SelectInput
                        label={label}
                        record={current_record}
                        action_word={action_word}
                        dispatch={dispatch}
                        display={display}
                    />
                )
            })
            .catch(err => {
                // Handle failure
            })

    }
}

@gaearon
Copy link
Contributor

gaearon commented May 26, 2017

I guess I’m thinking that if I render DetailsField 1000 times, then I’ll have 5 * 1000 = 5000 unnecessary modules imported that are taking up space in memory.

This is not a correct assumption. Imports are evaluated only once, and later cached.
For example, even though you import React in every component, it is only evaluated once.
Same with your own modules.

Moreover, rendering a component does not re-execute any code in top level scope. So it does not even get to the imports (which, again, are cached anyway).

@gaearon
Copy link
Contributor

gaearon commented May 26, 2017

Apart from my linter complaining, could I do this then?

No, you'd just create unused Promises. The return statement is inside a nested function so that function would return the React element, whereas the relevant_component function would not return anything.

I'm happy to discuss this more, but as a separate StackOverflow question. It's neither directly related to CRA, nor related to the problem you're solving (which as I've explained earlier is not really a problem, since you have incorrect assumptions).

@richierob62
Copy link
Author

Imports are evaluated only once, and later cached.
For example, even though you import React in every component, it is only evaluated once.
Same with your own modules.

Lightbulb moment! So, there's no advantage jumping through conditional import hoops. I actually thought React (and similar modules) were special cases. Didn't realize the same applied to my custom components.

Thanks!

@gaearon
Copy link
Contributor

gaearon commented May 26, 2017

Yea, exactly!

@lock lock bot locked and limited conversation to collaborators Jan 21, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants