question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[SelectField] Introduce new component

See original GitHub issue

Minimum example of React Material-UI with the only TextField component imported - result bundle size is 250Kb!

https://github.com/pqr/react-mui-treeshake-does-not-work

webpack-bundle-analyzer shows everything is imported, nothing is tree shaked.

According to documentation https://material-ui.com/guides/minimizing-bundle-size/ tree-shaking should work, quote:

Tree-shaking of Material-UI works out of the box in modern frameworks. Material-UI exposes its full API on the top-level material-ui import. If you’re using ES6 modules and a bundler that supports tree-shaking (webpack >= 2.x, parcel with a flag) you can safely use named imports and still get an optimised bundle size automatically:

import { Button, TextField } from '@material-ui/core';

But as shown in this minimum example it does not work out of the box. I did several experiments, read through issues and StackOverflow and failed to find a solution.

  • [+] The issue is present in the latest release.
  • [+] I have searched the issues of this repository and believe that this is not a duplicate.

Current Behavior 😯

Bundle size of minimum app with the only TextField imported is about 250Kb, everything is included into bundle (analized with webpack-bundle-analyzer

Expected Behavior 🤔

Bundle should not contain any code not related to the only imported TextField, tree shake to be working

Steps to Reproduce 🕹

git clone git@github.com:pqr/react-mui-treeshake-does-not-work.git
cd react-mui-treeshake-does-not-work
npm install
npm run build
npm run analyze

Your Environment 🌎

Tech Version
Material-UI v4.11
React 16.13.1
Browser Any
TypeScript No
Webpack 4.43

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:4
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
oliviertassinaricommented, Jul 17, 2020

@pqr wants his app to only include the code he needs. In his bundle size analysis

he noticed a lot of components with no clear relation to displaying an input. He went on assuming that it’s because tree shaking is broken. Wrong.

So from what I understand, we are covering the same root pain point with the SelectField component proposal. It’s only the interpretation of the origin of the pain from the author that is wrong, the underlying issue is still present: text field shouldn’t bundle select.

The proposed solution is:

-<TextField select />
+<SelectField />
1reaction
lovelle-cardosocommented, Aug 7, 2021

Our team was also really caught off guard by this. We noticed the bundle sizes for our form pages were really bloated despite us only using text fields and buttons from material-ui. Spent ages trying to track down where the bundler was getting confused and importing Selects and Modals, only to find that TextField includes Select, Modal, and their many dependencies by default.

For any other devs stumbling across this issue, here’s a bandaid solution that you can use to greatly reduce the size of TextFields while material-ui works on an official fix. This custom component simply follows the structure of material-ui’s TextField component but removes any references to Select and requires that the variant component is provided as a prop (This way, only the variant type you use will be included in your bundle, rather than all 3 standard, outlined, and filled variant components being included by default):

ComposableTextField.tsx

import React from "react";
import { InputProps } from "@material-ui/core/Input";
import { TextFieldProps } from "@material-ui/core/TextField";
import FormControl from "@material-ui/core/FormControl";
import InputLabel from "@material-ui/core/InputLabel";
import FormHelperText from "@material-ui/core/FormHelperText";

interface ComposableTextFieldProps
  extends Omit<TextFieldProps, "select" | "SelectProps" | "children"> {
  InputComponent: React.ComponentType<InputProps>;
}

const ComposableTextField = React.forwardRef(
  (props: ComposableTextFieldProps, ref: React.Ref<HTMLDivElement>): JSX.Element => {
    const { InputComponent, ...textFieldProps } = props;
    const {
      autoComplete,
      autoFocus = false,
      className,
      color = "primary",
      defaultValue,
      disabled = false,
      error = false,
      FormHelperTextProps,
      fullWidth = false,
      helperText,
      id,
      InputLabelProps,
      inputProps,
      InputProps,
      inputRef,
      label,
      maxRows,
      minRows,
      multiline = false,
      name,
      onBlur,
      onChange,
      onFocus,
      placeholder,
      required = false,
      rows,
      type,
      value,
      variant = "outlined",
      ...other
    } = textFieldProps;

    if (!InputComponent) {
      return null;
    }

    const InputMore: Record<string, unknown> = {};

    if (variant === "outlined") {
      if (InputLabelProps && typeof InputLabelProps.shrink !== "undefined") {
        InputMore.notched = InputLabelProps.shrink;
      }
      if (label) {
        const displayRequired = InputLabelProps?.required ?? required;
        InputMore.label = (
          <>
            {label}
            {displayRequired && "\u00a0*"}
          </>
        );
      }
    }

    const helperTextId = helperText && id ? `${id}-helper-text` : undefined;
    const inputLabelId = label && id ? `${id}-label` : undefined;

    return (
      <FormControl
        className={["MuiTextField-root", className].filter(Boolean).join(" ")}
        disabled={disabled}
        error={error}
        fullWidth={fullWidth}
        ref={ref}
        required={required}
        color={color}
        variant={variant}
        {...other}
      >
        {label && (
          <InputLabel htmlFor={id} id={inputLabelId} {...InputLabelProps}>
            {label}
          </InputLabel>
        )}
        <InputComponent
          aria-describedby={helperTextId}
          autoComplete={autoComplete}
          autoFocus={autoFocus}
          defaultValue={defaultValue}
          fullWidth={fullWidth}
          multiline={multiline}
          name={name}
          rows={rows}
          maxRows={maxRows}
          minRows={minRows}
          type={type}
          value={value}
          id={id}
          inputRef={inputRef}
          onBlur={onBlur}
          onChange={onChange}
          onFocus={onFocus}
          placeholder={placeholder}
          inputProps={inputProps}
          {...InputMore}
          {...InputProps}
        />
        {helperText && (
          <FormHelperText id={helperTextId} {...FormHelperTextProps}>
            {helperText}
          </FormHelperText>
        )}
      </FormControl>
    );
  }
);

export default ComposableTextField;

Simply replace any references to TextField with this ComposableTextField component instead. And make sure to pass in the InputComponent variant you’d like to use:

Replace this:

<TextField label="Outlined" variant="outlined" />

With this:

<ComposableTextField label="Outlined" variant="outlined" InputComponent={OutlinedInput} />

As a side note, the material-ui Text Fields documentation does mention that you can use the lower level components to better customize your TextFields, but perhaps it would also be worth mentioning that you can use these lower level components to reduce your bundle size in the Minimizing Your Bundle Size documentation.

Read more comments on GitHub >

github_iconTop Results From Across the Web

[SelectField] Introduce new component · Issue #21782 - GitHub
Material-UI exposes its full API on the top-level material-ui import. If you're using ES6 modules and a bundler that supports tree-shaking ( ...
Read more >
The SelectField Component - React-admin - Marmelab
When you need to display an enumerated field, <SelectField> maps the value to a string. Usage. For instance, if the gender field can...
Read more >
Get a select field to show other select fields based on id of ...
I am working with material-ui and React. I have a SelectField that is part of a component which is a grandchild of the...
Read more >
How to use Material UI Select in React - Refine Dev
Below is a simple illustration of how to use the Material UI Select component in your next React project: import * as React...
Read more >
SelectField | ExtReact 6.5.3 - Sencha Documentation
This will create a mask underneath the Component that covers its parent and does not allow the user to interact with any other...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found