【问题标题】:MaterialUI TextField lag issue. How to improve performance when the form has many inputs?Material UI TextField 滞后问题。当表单有很多输入时如何提高性能?
【发布时间】:2020-11-15 01:28:47
【问题描述】:

我在使用 MateriaUI 框架的 Textfield 时遇到了一个恼人的问题。我有一个包含许多输入的表单,在字段内键入或删除值时似乎有点滞后。在其他组件中,当有 2 或 3 个输入时,根本没有延迟。

编辑:问题似乎出在我的 onChange 处理程序上。

非常感谢任何帮助。提前致谢。

这是我的自定义输入代码:

import React, { useReducer, useEffect } from 'react';
import { validate } from '../utils/validators';
import TextField from '@material-ui/core/TextField';
import { ThemeProvider, makeStyles, createMuiTheme } from '@material-ui/core/styles';
import { green } from '@material-ui/core/colors';

const useStyles = makeStyles((theme) => ({
    root: {
        color: 'white'
    },
    input: {
        margin: '10px',
        '&& .MuiInput-underline:before': {
            borderBottomColor: 'white'
        },
    },
    label: {
        color: 'white'
    }
}));

const theme = createMuiTheme({
    palette: {
        primary: green,
    },
});


const inputReducer = (state, action) => {
    switch (action.type) {
        case 'CHANGE':
            return {
                ...state,
                value: action.val,
                isValid: validate(action.val, action.validators)
            };
        case 'TOUCH': {
            return {
                ...state,
                isTouched: true
            }
        }
        default:
            return state;
    }
};

const Input = props => {
    const [inputState, dispatch] = useReducer(inputReducer, {
        value: props.initialValue || '',
        isTouched: false,
        isValid: props.initialValid || false
    });

    const { id, onInput } = props;
    const { value, isValid } = inputState;

    useEffect(() => {
        onInput(id, value, isValid)
    }, [id, value, isValid, onInput]);

    const changeHandler = event => {
        dispatch({
            type: 'CHANGE',
            val: event.target.value,
            validators: props.validators
        });
    };

    const touchHandler = () => {
        dispatch({
            type: 'TOUCH'
        });
    };

    const classes = useStyles();

    return (
        <ThemeProvider theme={theme}>
            <TextField
                className={classes.input}
                InputProps={{
                    className: classes.root
                }}
                InputLabelProps={{
                    className: classes.label
                }}
                id={props.id}
                type={props.type}
                label={props.label}
                onChange={changeHandler}
                onBlur={touchHandler}
                value={inputState.value}
                title={props.title}
                error={!inputState.isValid && inputState.isTouched}
                helperText={!inputState.isValid && inputState.isTouched && props.errorText}
            />
        </ThemeProvider>
    );
};

export default Input;

【问题讨论】:

  • 您是否使用自动调整文本区域?大多数自动调整文本区域在读取scrollHeight 以计算其高度时会强制浏览器重排。浏览器重排是一个很大的性能问题,会导致明显的延迟。
  • 嗨@blid!不,我没有使用自动调整大小

标签: javascript reactjs material-ui


【解决方案1】:

确保提取渲染范围之外的所有常量值。

例如,您向InputLabelPropsInputProps 提供新对象的每次渲染都会强制重新渲染子组件。

所以每个不是必须在功能组件内创建的新对象,你应该在外部提取,

这包括:

        const touchHandler = () => {
            dispatch({
                type: 'TOUCH'
            });
        };
    
        const useStyles = makeStyles((theme) => ({
            root: {
                display: 'flex',
                flexWrap: 'wrap',
                color: 'white'
            },
            input: {
                margin: '10px',
                '&& .MuiInput-underline:before': {
                    borderBottomColor: 'white'
                },
            },
            label: {
                color: 'white'
            }
        }));
    
        const theme = createMuiTheme({
            palette: {
                primary: green,
            },
        });
    

您也可以使用 react memo 进行功能组件优化,似乎适合您的情况。

【讨论】:

  • 嗨@jony89,感谢您的回答。我在答案中更新了我的代码。我认为const classes = useStyles();const touchHandler = () =&gt; { 不能在功能组件之外。在第一种情况下,存在无效的挂钩调用,在第二种情况下,未定义调度。然而,它现在似乎好一点,但仍然存在滞后。你能提供一个备忘录的例子吗?我从来没有用过它。如果你愿意:)
  • 嘿,创建 muiTheme 最多应该是顶级反应组件级别。绝对不在输入组件内。关于反应备忘录,您可以在此处查看示例reactjs.org/docs/react-api.html
  • 问题似乎出在我的 onChange 处理程序上。如果我禁用它,则没有延迟。在我使用 materialUI 设置输入样式之前,它没有给我带来任何问题。
  • 这是因为每次按键的更改事件是触发重新渲染的事件。这很好,但是,您在每次渲染时重新创建不必要的对象,这可能是导致延迟的原因。
【解决方案2】:

除了@jony89 的回答。您可以尝试以下 1 种其他解决方法。

  1. 在每次按键时 (onChange) 更新本地状态。
  2. 在模糊事件时调用父级的更改处理程序

       const Child = ({ parentInputValue, changeValue }) => {
      const [localValue, setLocalValue] = React.useState(parentInputValue);
      return <TextInputField
        value={localValue}
        onChange={(e) => setLocalValue(e.target.value)} 
        onBlur={() => changeValue(localValue)} />;

    }

    const Parent = () => {
        const [valMap, setValMap] = React.useState({
          child1: '',
          child2: ''
        });
        return (<>
            <Child parentInputValue={valMap.child1} changeValue={(val) => setValMap({...valMap, child1: val})}
            <Child parentInputValue={valMap.child2} changeValue={(val) => setValMap({...valMap, child2: val})}
        </>
        );
    }

如果您不想重构现有代码,这将解决您的问题。

但实际的解决方法是拆分状态,以便 child1 状态的更新不会影响 child2 的(更改引用或变异)状态。

【讨论】:

    【解决方案3】:

    我设法通过用来自react-hook-form 替换普通的 来摆脱这种滞后效应。

    打字滞后的旧代码

    <Grid item xs={12}>
        <TextField
            error={descriptionError.length > 0}
            helperText={descriptionError}
            id="outlined-textarea"
            onChange={onDescriptionChange}
            required
            placeholder="Nice placeholder"
            value={description}
            rows={4}
            fullWidth
            multiline
        />
    </Grid>
    

    使用 react-hook-form 更新代码

    
    import { FormProvider, useForm } from 'react-hook-form';
    import { yupResolver } from '@hookform/resolvers/yup';
    
    const methods = useForm({
        resolver: yupResolver(validationSchema)
    });
    const { handleSubmit, errors, reset } = methods;
    
    const onSubmit = async (entry) => {
        console.log(`This is the value entered in TextField ${entry.name}`);
    };
    
    <form onSubmit={handleSubmit(onSubmit)}>
        <Grid item xs={12}>
            <FormProvider fullWidth {...methods}>
                <DescriptionFormInput
                    fullWidth
                    name="name"
                    placeholder="Nice placeholder here"
                    size={matchesXS ? 'small' : 'medium'}
                    bug={errors}
                />
            </FormProvider>
        </Grid>
        <Button
            type="submit"
            variant="contained"
            className={classes.btnSecondary}
            startIcon={<LayersTwoToneIcon />}
            color="secondary"
            size={'small'}
            sx={{ mt: 0.5 }}
        >
            ADD
        </Button>
    </form>
    
    import React from 'react';
    import PropTypes from 'prop-types';
    import { Controller, useFormContext } from 'react-hook-form';
    
    import { FormHelperText, Grid, TextField } from '@material-ui/core';
    
    const DescriptionFormInput = ({ bug, label, name, required, ...others }) => {
        const { control } = useFormContext();
    
        let isError = false;
        let errorMessage = '';
        if (bug && Object.prototype.hasOwnProperty.call(bug, name)) {
            isError = true;
            errorMessage = bug[name].message;
        }
    
        return (
            <>
                <Controller
                    as={TextField}
                    name={name}
                    control={control}
                    defaultValue=""
                    label={label}
                    fullWidth
                    InputLabelProps={{
                        className: required ? 'required-label' : '',
                        required: required || false
                    }}
                    error={isError}
                    {...others}
                />
                {errorMessage && (
                    <Grid item xs={12}>
                        <FormHelperText error>{errorMessage}</FormHelperText>
                    </Grid>
                )}
            </>
        );
    };
    

    通过使用 react-hook-form,我可以让 TextField 比以前更灵敏。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-20
      • 1970-01-01
      • 2020-02-26
      • 1970-01-01
      相关资源
      最近更新 更多