Очистка определенного состояния редукции перед рендерингом компонента

У меня есть следующая кнопка «Купить» для корзины.

У меня также есть компонент под названием Tooltip, который будет отображаться для сообщений об ошибках/успехах. Он использует ширину кнопки, чтобы определить ее центральную точку. Следовательно, я использую `ref, так как мне нужно получить доступ к его физическому размеру в DOM. Я читал, что использование атрибута ref — плохая новость, но я не уверен, как еще можно выполнить позиционирование дочернего компонента, основанного на физическом DOM. Но это уже другой вопрос... ;)

Я сохраняю состояние приложения в localStorage. Как видно здесь: https://egghead.io/lessons/javascript-redux-persisting-the-state-to-the-local-storage

Проблема, с которой я сталкиваюсь, заключается в том, что перед рендерингом мне нужно очистить свойство success состояния. В противном случае, если у меня есть сообщение об успехе в состоянии, при начальном рендеринге () Tooltip также попытается выполнить рендеринг. Это невозможно, так как кнопка, на которую он опирается, еще не находится в DOM.

Я думал, что очистка состояния успеха с помощью действия Redux в componentWillMount очистит состояние успеха и, следовательно, устранит проблему, но похоже, что метод render() не распознает, что состояние было изменено, и по-прежнему будет показывать старое значение в console.log().

Мой обходной путь - проверить, существует ли кнопка, а также сообщение об успехе: showSuccessTooltip && this.addBtn

Почему render() не распознает изменение состояния componentWillMount()?

Вот класс ProductBuyBtn.js:

import React, { Component } from 'react';
import { connect } from 'react-redux'

// Components
import Tooltip from './../utils/Tooltip'

// CSS
import './../../css/button.css'

// State
import { addToCart, clearSuccess } from './../../store/actions/cart'

class ProductBuyBtn extends Component {

	componentWillMount(){
		this.props.clearSuccess()
	}

	addToCart(){
		this.props.addToCart(process.env.REACT_APP_SITE_KEY, this.props.product.id, this.props.quantity)
	}

	render() {

		let showErrorTooltip = this.props.error !== undefined
		let showSuccessTooltip = this.props.success !== undefined

		console.log(this.props.success)

		return (
			<div className="btn_container">
				<button className="btn buy_btn" ref={(addBtn) => this.addBtn = addBtn } onClick={() => this.addToCart()}>Add</button>
				{showErrorTooltip && this.addBtn &&
					<Tooltip parent={this.addBtn} type={'dialog--error'} messageObjects={this.props.error} />
				}
				{showSuccessTooltip && this.addBtn &&
					<Tooltip parent={this.addBtn} type={'dialog--success'} messageObjects={{ success: this.props.success }} />
				}
			</div>
		);
	}
}

function mapStateToProps(state){
	return {
		inProcess: state.cart.inProcess,
		error: state.cart.error,
		success: state.cart.success
	}
}

const mapDispatchToProps = (dispatch) => {
	return {
		addToCart: (siteKey, product_id, quantity) => dispatch(addToCart(siteKey, product_id, quantity)),
		clearSuccess: () => dispatch(clearSuccess())
	}
}

export default connect(mapStateToProps, mapDispatchToProps)(ProductBuyBtn)


person Gurnzbot    schedule 31.08.2017    source источник


Ответы (1)


Что ж, похоже, это известная проблема, с которой легко столкнуться (сложнее избавиться, особенно красивым/не хакерским способом). См. эта сверхдлинная ветка).

Проблема в том, что отправка действия в componentWillMount, которое (в конечном счете) изменяет свойства, входящие в компонент, не гарантирует, что действие имело место до первого рендеринга.

Таким образом, в основном render() не ждет, пока ваше отправленное действие вступит в силу, он рендерится один раз (со старыми реквизитами), затем действие вступает в силу и изменяет реквизиты, и затем компонент перерисовывается с новый реквизит.

Таким образом, вам нужно либо делать то, что вы уже делаете, либо использовать внутреннее состояние компонентов, чтобы отслеживать, является ли это первым рендерингом или нет, что-то вроде этот комментарий. Есть и другие предложения, но я не могу перечислить их все.

person jonahe    schedule 31.08.2017
comment
Спасибо. Я не был уверен, существует ли «лучшая практика» или нет. Вы изложили мои первоначальные мысли по этому поводу в том смысле, что компонент визуализируется до того, как его состояние фактически изменится. Это странно, потому что я сделал console.log в редьюсере, чтобы увидеть, когда он срабатывает, и, похоже, это произошло до рендеринга. Я думаю, это обман. - person Gurnzbot; 31.08.2017
comment
Без проблем! Я должен сказать, что я не эксперт по Redux, поэтому я рекомендую хотя бы взглянуть на ветку, на которую я ссылался, просто чтобы убедиться, что я не исказил содержание. Но я помню, как сталкивался с подобными ситуациями раньше (и я не помню, чтобы они когда-либо получали чистое решение). Я не слишком удивлюсь, если окажется, что проблемы можно избежать, если приложение структурировано каким-то другим образом, но я не знаю, как это сделать (и, насколько я мог , или, по крайней мере, не было единого мнения о том, как это сделать.) - person jonahe; 31.08.2017
comment
Я быстро просмотрел тему, которую вы разместили (спасибо!). Кажется, что все действительно делают обходные пути. Может быть, на самом деле это не обходной путь, потому что это просто то, как это делается ;) Это один из тех случаев, когда я рад, что у всех есть эта проблема, и это не я хромой разработчик. - person Gurnzbot; 01.09.2017
comment
Ха-ха, я знаю это чувство :) - person jonahe; 01.09.2017
comment
Я просто сводил себя с ума на несколько часов, прежде чем решил просто очистить весь штат, прежде чем даже изменить маршрут. Это исправило это, но взломано. - person Z2VvZ3Vp; 07.11.2018