web-dev-qa-db-fra.com

React + Redux - Input onChange est très lent lors de la saisie lorsque l'entrée a une valeur de l'état

J'ai obtenu mon entrée qui est remplie par une valeur de mon état.

<input id="flashVars" name="flashVars" type="text" value={settings.flashVarsValue} disabled={isDisabled} onChange={handleChange} />

Settingsest mon état avec Redux. Lorsque je mets une valeur dans mon entrée, je dois spécifier une fonction onChange. Voici ma fonction onChange:

handleFlashVarsChange(e) {
  let { dispatch } = this.props;

  dispatch( changeFlashVarsValue(e.target.value) );
}

Il modifie la valeur d'état flashVarsValue pour la valeur de l'entrée. Mais quand je tape mon entrée, ça traîne. Je ne comprends pas pourquoi je dois appeler la répartition chaque fois que je modifie la valeur d'entrée.

Y a-t-il un moyen de donner moins de retards?

Mon réducteur:

import { ACTIONS } from '../utils/consts';

const initialState = {
  ...
  flashVarsValue: '',
  ...
};

export function formSettings(state = initialState, action = '') {
  switch (action.type) {

    ...

    case ACTIONS.CHANGE_FLASHVARS_VALUE:
      return Object.assign({}, state, {
        flashVarsValue: action.data
      });

    default:
      return state;
  }
}

Mon action:

export function changeFlashVarsValue(data) {
  return {
    type: ACTIONS.CHANGE_FLASHVARS_VALUE,
    data: data
  }
}

Merci

39
Mike Boutin

J'ai eu un problème similaire lorsque je modifiais une grille avec un million de lignes, donc ce que j'ai fait était de changer la logique de mise à jour, dans votre cas, handleChange ne devrait être appelé que sur l'événement `` onBlur '' au lieu de onChange. Cela ne déclenchera la mise à jour que lorsque vous perdrez le focus. Mais je ne sais pas si ce serait une solution satisfaisante pour vous ..

15
luanped

Le problème ici est peut-être un nouveau rendu. Vous passez des "paramètres" (votre état entier) à votre composant contenant "l'entrée", et nous ne savons pas comment les autres composants connectés sont couplés à l'état. Vérifiez si, à la suite de la mutation de l'objet d'état, vous rendez bien plus que la simple entrée à chaque frappe. La solution à cela est de passer plus directement dans les parties spécifiques de l'état dont vous avez besoin de mapStateToProps (dans ce cas, ne passez peut-être que "flashVarsValue" si c'est tout ce dont ce composant a besoin, et assurez-vous que d'autres composants ne sont pas également transmis dans leur intégralité). état) et utilisez PureRenderMixin ou Dan Abramov https://github.com/gaearon/react-pure-render si vous utilisez des composants ES6 pour ne pas restituer si vos accessoires n'ont pas changé

4
Royi Hagigi

La réponse pour moi a été d'utiliser le hook de cycle de vie shouldComponentUpdate. Cela a déjà été donné comme réponse dans un commentaire de Mike Boutin (il y a environ un an :)), mais un exemple pourrait aider le prochain visiteur ici.

J'ai eu un problème similaire, la saisie de texte étant perdue et lente et nerveuse. J'utilisais setState pour mettre à jour le formData dans mon événement onChange.

J'ai trouvé que le formulaire effectuait un nouveau rendu complet à chaque pression de touche, car l'état avait changé. Donc, pour arrêter cela, j'ai annulé la fonction:

shouldComponentUpdate(nextProps, nextState) {
   return this.state.formErrors !== nextState.formErrors);
}

J'affiche un panneau de notification d'erreur lors de la soumission du formulaire avec des erreurs de validation nouvelles ou modifiées, et c'est la seule fois que j'ai besoin de restituer.

Si vous n'avez aucun composant enfant, vous pouvez probablement simplement définir le shouldComponentUpdate du composant de formulaire pour toujours retourner false.

4
ben.tiberius.avery

La réponse n'est pas de restituer votre composant à chaque frappe de touche, uniquement si l'utilisateur arrête de taper. Quelque chose comme ceci:

shouldComponentUpdate(nextProps, nextState) {
    if (!textInputReRender)
        return false;
    else
        return true;
}

onTextInputChange = (propName, propValue) => {
    if (inputChangeTimerId)
        clearTimeout(inputChangeTimerId);

    inputChangeTimerId = setTimeout(() => {
        inputChangeTimerId = null;
        const newState = {};

        textInputReRender = true;

        newState[propName] = propValue;
        this.setState(newState);
    }, 500);

    textInputReRender = false;
}
2
Gustav.Calder

Je sais que c'est une vieille question, mais si vous voulez déclencher onChange sur une entrée de texte, vous voulez probablement rebondir votre événement. Ce fil fait un bon travail pour le décomposer, mais je pense que cela fonctionnerait pour l'exemple de l'op:

import debounce from 'debounce'                                      

function debounceEventHandler(...args) {
  const debounced = debounce(...args)
  return function (e) {
    e.persist();
    return debounced(e);
  }
}                                                                      
const Container = React.createClass({
  handleFlashVarsChange(e) {
    let { dispatch } = this.props;
    //basic redux stuff
    this.props.changeFlashVarsValue(e.target.value));
  },
  render() {
    const handleChange = debounceEventHandler(this.handleFlashVarsChange, 15);
    return (
      <input id="flashVars" onChange={handleChange} />
    )
  }                                                                         
}
//...prep and return your redux container
2
Joe