React-redux: должен ли рендер всегда происходить в том же ТИКе, что и отправка действия?



В моем приложении react-redux есть управляемый ввод текста. Каждый раз, когда компонент изменяет значение, он отправляет действие, и в конце концов значение возвращается через цикл redux и визуализируется.



В приведенном ниже примере это работает хорошо, но на практике я столкнулся с проблемой, когда рендеринг происходит асинхронно из диспетчера действий, и ввод теряет позицию курсора. Чтобы продемонстрировать проблему, я добавил еще один вход с задержкой, явно введенной. Добавление a пробел в середине слова заставляет курсор пропустить асинхронный ввод.



У меня есть две теории на этот счет, и я хотел бы знать, какая из них верна:


  • это должно работать, но у меня есть ошибка где-то в моем производственном приложении, которая вызывает задержку

  • тот факт, что он работает в простом примере, - это просто удача, и react-redux не гарантирует, что рендеринг произойдет синхронно


Кто из них прав?



Рабочий пример:



Http://jsbin.com/doponibisi/edit?html,js, выход



const INITIAL_STATE = {
value: ""
};

const reducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SETVALUE':
return Object.assign({}, state, { value: action.payload.value });
default:
return state;
}
};

const View = ({
value,
onValueChange
}) => (
<div>
Sync: <input value={value} onChange={(e) => onValueChange(e.target.value)} /><br/>
Async: <input value={value} onChange={(e) => { const v = e.target.value; setTimeout(() => onValueChange(v), 0)}} />
</div>
);

const mapStateToProps = (state) => {
return {
value: state.value
};
}

const mapDispatchToProps = (dispatch) => {
return {
onValueChange: (value) => {
dispatch({
type: 'SETVALUE',
payload: {
value
}
})
}
};
};


const { connect } = ReactRedux;
const Component = connect(
mapStateToProps,
mapDispatchToProps
)(View);

const { createStore } = Redux;
const store = createStore(reducer);

ReactDOM.render(
<Component store={store} />,
document.getElementById('root')
);


EDIT: уточняющий вопрос



Марко и Натан оба правильно указали, что это известная проблема в React, которая не будет исправлена. Если существует setTimeout или другая задержка между onChange и установкой значения, положение курсора будет потеряно.

Однако того факта, что setState просто планирует обновление, недостаточно, чтобы вызвать эту ошибку. В выпуске Github то, что Марко связал, есть комментарий:


Грубо говоря, setState не откладывает рендеринг, это пакетная обработка
обновляет и выполняет их немедленно, когда текущее задание React имеет
закончено, не будет никакого кадра рендеринга между ними. Так что в каком-то смысле,
операция выполняется синхронно по отношению к текущей визуализации
рамка. setTimeout планирует его для другого кадра рендеринга.




Это можно увидеть в Примере JsBin: версия" sync " также использует setState, но все работает.



Открытым остается вопрос: есть ли что-то внутри Redux, что создает задержку, которая позволяет кадру рендеринга быть промежуточным, или Redux может быть использован таким образом, чтобы избежать этих задержек?



Обходные пути для рассматриваемой проблемы не нужны, я нашел тот, который работает в моем случае, но мне интересно узнать ответ на более общий вопрос.



EDIT: проблема решена



Я был доволен ответом Кларкса и даже удостоился награды, но оказалось, что это было неправильно, когда я действительно протестировал его, удалив все промежуточные программы. Я также нашел проблему github, которая связана с этим.



Https://github.com/reactjs/react-redux/issues/525



Ответ таков:




  • это проблема в react-redux, которая будет исправлена с react-redux 5.1 и react v16

653   4  

4 ответов:

, что промежуточное программное обеспечение вы используете в приложении "возвращение"? Возможно, один из них оборачивает обещание вокруг ваших депеш действий. Использование Redux без промежуточного ПО не демонстрирует такого поведения, поэтому я думаю, что это, вероятно, что-то конкретное для вашей установки.

Проблема не связана с Redux,а с реакцией. Это известная проблема и не будет исправлена в ядре React, поскольку это не считается ошибкой, а "неподдерживаемой функцией".

Этот ответ прекрасно объясняет сценарий.

Некоторые попытки решить эту проблему были сделаны, но, как вы можете видеть, все они включают компонент оболочки вокруг входного сигнала, так что это очень неприятное решение, если вы спросите меня.

Асинхронное обновление без потери позиции никогда не поддерживалось

--- дан Абрамов (гаэрон)

Решение состоит в том, чтобы отслеживать положение курсора и использовать ref внутри componentDidUpdate() для правильного размещения курсора.


Дополнительная информация:

Когда вы устанавливаете атрибуты в react, внутренне это происходит:

node.setAttribute(attributeName, '' + value);

Когда вы задаете value таким образом, поведение будет непоследовательным:

Использование setAttribute() для изменения некоторых атрибутов, особенно value в XUL, работает непоследовательно, так как атрибут задает значение по умолчанию.

--- https://developer.mozilla.org/en/docs/Web/API/Element/setAttribute


Что касается вашего вопроса о том, происходит ли рендеринг синхронно, react setState() является асинхронным и используется внутренне react-redux:

Нет никакой гарантии синхронной работы вызовов setState и вызовов может дозироваться для повышения производительности

--- https://facebook.github.io/react/docs/react-component.html#setstate

Существует внутренняя шутка в команде, что React следовало бы назвать "расписанием", потому что React не хочет быть полностью "реактивным".

--- https://facebook.github.io/react/contributing/design-principles.html#scheduling

Я думаю, что react-redux и redux совершенно не имеют отношения к вашему случаю, это чистое поведение React. React-redux в конечном счете вызывает setState на вашем компоненте, нет никакой магии.

Проблема в том, что ваш асинхронный setState создает фрейм рендеринга между рендерингом react и собственным событием браузера, заключается в том, что механизм пакетного обновления происходит только в обработчике синтетических событий React и методах жизненного цикла. Можете проверить этот пост для деталей.

Comments

    Ничего не найдено.