setState () внутри componentDidUpdate()


Я пишу скрипт, который перемещает выпадающий список ниже или выше ввода в зависимости от высоты выпадающего списка и положения ввода на экране. Также я хочу установить модификатор в выпадающее меню в соответствии с его направлением. Но с помощью setState внутри componentDidUpdate создает бесконечный цикл(что очевидно)

Я нашел решение в использовании getDOMNode и установка classname в выпадающее меню напрямую, но я чувствую, что должно быть лучшее решение с использованием инструментов React. Кто-нибудь может помочь я?

вот часть рабочего кода с getDOMNode (i немного пренебрегли логикой позиционирования для упрощения кода)

let SearchDropdown = React.createClass({
    componentDidUpdate(params) {
        let el = this.getDOMNode();
        el.classList.remove('dropDown-top');
        if(needToMoveOnTop(el)) {
            el.top = newTopValue;
            el.right = newRightValue;
            el.classList.add('dropDown-top');
        }
    },
    render() {
        let dataFeed = this.props.dataFeed;
        return (
            <DropDown >
                {dataFeed.map((data, i) => {
                    return (<DropDownRow key={response.symbol} data={data}/>);
                })}
            </DropDown>
        );
    }
});

а вот код с setstate (который создает бесконечный цикл)

let SearchDropdown = React.createClass({
    getInitialState() {
        return {
            top: false
        };
    },
    componentDidUpdate(params) {
        let el = this.getDOMNode();
        if (this.state.top) {
           this.setState({top: false});
        }
        if(needToMoveOnTop(el)) {
            el.top = newTopValue;
            el.right = newRightValue;
            if (!this.state.top) {
              this.setState({top: true});
           }
        }
    },
    render() {
        let dataFeed = this.props.dataFeed;
        let class = cx({'dropDown-top' : this.state.top});
        return (
            <DropDown className={class} >
                {dataFeed.map((data, i) => {
                    return (<DropDownRow key={response.symbol} data={data}/>);
                })}
            </DropDown>
        );
    }
});
5 54

5 ответов:

можно использовать setStateвнутри componentDidUpdate. Проблема в том, что каким-то образом вы создаете бесконечный цикл, потому что нет условия останова.

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

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

componentDidUpdate: function() {
    if (condition) {
        this.setState({..})
    } else {
        //do something else
    }
}

если вы только обновляете компонент, отправляя ему реквизиты (он не обновляется setState, за исключением случая внутри componentDidUpdate), вы можете вызвать setState внутри componentWillReceiveProps вместо componentDidUpdate.

The componentDidUpdate подпись void::componentDidUpdate(previousProps, previousState). С помощью этого вы сможете проверить, какие реквизиты / состояние грязные и позвонить setState соответственно.

пример:

componentDidUpdate(previousProps, previousState) {
    if (previousProps.data !== this.props.data) {
        this.setState({/*....*/})
    }
}

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

убедитесь, что вы установили свое состояние следующим образом:

let top = newValue /*true or false*/
if(top !== this.state.top){
    this.setState({top});
}

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