React + Redux — Замедление работы onChange в Input при вводе, когда значение берется из состояния
Я столкнулся с проблемой при работе с инпутом, значение которого заполняется из состояния Redux. У меня есть следующий код для инпута:
<input id="flashVars" name="flashVars" type="text" value={settings.flashVarsValue} disabled={isDisabled} onChange={handleChange} />
Значение flashVarsValue
— это часть моего состояния, и я использую функцию onChange
, чтобы обновлять его. Вот как выглядит моя функция onChange
:
handleFlashVarsChange(e) {
let { dispatch } = this.props;
dispatch(changeFlashVarsValue(e.target.value));
}
Эта функция срабатывает каждый раз, когда я изменяю значение инпута, и обновляет значение flashVarsValue
в состоянии. Однако при вводе текста в инпут наблюдается задержка (лаг). Я не понимаю, почему я должен вызывать dispatch
каждый раз, когда меняю значение инпута.
Существует ли способ уменьшить лаги при вводе текста?
Также вот мой редюсер:
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;
}
}
И вот моя экшен:
export function changeFlashVarsValue(data) {
return {
type: ACTIONS.CHANGE_FLASHVARS_VALUE,
data: data,
};
}
Спасибо!
5 ответ(ов)
У меня была аналогичная проблема, когда я редактировал сетку с миллионом строк. Я изменил логику обновления, и в вашем случае рекомендую вызывать handleChange
только при событии onBlur
, а не onChange
. Это позволит производить обновление только в момент потери фокуса. Однако не уверен, подойдет ли это решение в вашем случае.
Проблема здесь, вероятно, связана с повторными рендерингами. Вы передаёте весь объект "settings" (ваше состояние) в компонент, содержащий "input", и нам не известно, как остальные компоненты связаны с состоянием. Проверьте, не происходит ли из-за изменения объекта состояния повторный рендеринг гораздо большего количества компонентов, чем просто "input", при каждом нажатии клавиши. Решением будет более целенаправленно передавать только те части состояния, которые вам нужны, из функции mapStateToProps (в данном случае, возможно, только "flashVarsValue", если это всё, что нужно этому компоненту) и убедитесь, что другим компонентам не передаётся всё состояние. Также вы можете использовать PureRenderMixin или библиотеку Дэна Абрамова (https://github.com/gaearon/react-pure-render), если используете компоненты ES6, чтобы избежать повторного рендеринга, если ваши свойства не изменились.
Ответ на мой вопрос заключался в использовании жизненного цикла shouldComponentUpdate. Это уже упоминалось в комментарии от Майка Бутена (примерно год назад 😃), но пример может помочь следующему посетителю.
У меня была похожая проблема: текстовое поле теряло введенные данные, а также работало медленно и дергалось. Я использовал setState для обновления formData в обработчике onChange.
Я обнаружил, что форма выполняла полное перерисовывание при каждом нажатии клавиши, так как состояние изменялось. Чтобы остановить это, я переопределил функцию:
shouldComponentUpdate(nextProps, nextState) {
return this.state.formErrors !== nextState.formErrors;
}
Я показываю панель уведомлений об ошибках при отправке формы с любыми новыми или измененными ошибками валидации, и именно в это время мне нужно перерисовывать компонент.
Если у вас нет дочерних компонентов, вы могли бы просто установить shouldComponentUpdate вашего компонента формы так, чтобы он всегда возвращал false.
Ответ заключается в том, чтобы не перерисовывать компонент при каждом нажатии клавиши, а только тогда, когда пользователь прекращает ввод. Примерный код может выглядеть так:
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;
}
Таким образом, компонент будет перерисовываться только после того, как пользователь перестанет вводить текст на определённый промежуток времени (в данном случае 500 миллисекунд).
В вашем коде есть ошибка в использовании свойства onChangeText
компонента TextInput
. Вы пытаетесь вызвать функцию setKeyword(t)
напрямую при рендеринге, что является некорректным подходом. Вместо этого следует передать функцию, которая будет вызвана при изменении текста.
Вот исправленный код:
import { TextInput, View } from "react-native";
import { connect } from "react-redux";
import React from "react";
function SearchTextInput(props) {
const { keyword = "", setKeyword } = props;
return (
<TextInput
style={styles.searchInputText}
placeholder={"placeholder here"}
value={keyword}
// Здесь передаем функцию, которая принимает текст и вызывает setKeyword
onChangeText={text => setKeyword(text)}
/>
);
}
const mapStateToProps = state => {
return {
keyword: state.search.keyword,
search: state.search
};
};
const mapDispatchToProps = dispatch => ({
setKeyword: payload => dispatch({type: 'updateSearchText', keyword: payload })
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(SearchTextInput);
Обратите внимание, что теперь функция onChangeText
принимает параметр text
, который затем передается в setKeyword
. Это позволит корректно обновлять значение keyword
в вашем состоянии Redux при изменении текста в поле ввода.
Как реализовать дебаунс?
Обнаружена ошибка: Невозможное нарушение: Неверный тип элемента: ожидался строковый тип (для встроенных компонентов) или класс/функция, но получен объект
Добавление тега script в React/JSX
"Не используйте Route или withRouter() вне Router при работе с react-router 4 и styled-components в React"
Использование IndexRoute в react-router v4 с React