У меня есть приложение, где мне нужно установить высоту элемента (скажем, "приложение-контент" ) динамически. Он принимает высоту "хром" приложения и вычитает его, а затем устанавливает высоту "содержимого приложения", чтобы соответствовать 100% в пределах этих ограничений. Это очень просто с ванильными JS, jQuery или Backbone-представлениями, но я изо всех сил пытаюсь понять, какой будет правильный процесс для этого в React?
Ниже приведен пример компонента. Я хочу, чтобы установить высоту app-content
равным 100% от окна за вычетом размера ActionBar
и BalanceBar
, но как узнать, когда все будет рендерировано, и где бы я поместил материал вычисления в этот Класс реагирования?
/** @jsx React.DOM */
var List = require('../list');
var ActionBar = require('../action-bar');
var BalanceBar = require('../balance-bar');
var Sidebar = require('../sidebar');
var AppBase = React.createClass({
render: function () {
return (
<div className="wrapper">
<Sidebar />
<div className="inner-wrapper">
<ActionBar title="Title Here" />
<BalanceBar balance={balance} />
<div className="app-content">
<List items={items} />
</div>
</div>
</div>
);
}
});
module.exports = AppBase;
https://facebook.github.io/react/docs/react-component.html#componentdidmount
Этот метод вызывается один раз после рендеринга вашего компонента. Таким образом, ваш код будет выглядеть так.
var AppBase = React.createClass({
componentDidMount: function() {
var $this = $(ReactDOM.findDOMNode(this));
// set el height and width etc.
},
render: function () {
return (
<div className="wrapper">
<Sidebar />
<div className="inner-wrapper">
<ActionBar title="Title Here" />
<BalanceBar balance={balance} />
<div className="app-content">
<List items={items} />
</div>
</div>
</div>
);
}
});
componentDidUpdate
если значения могут измениться после первого рендеринга.
componentDidMount()
не вызывает переход.
Один из недостатков использования componentDidUpdate
или componentDidMount
заключается в том, что они фактически выполняются до того, как элементы dom будут сделаны, но после того, как они были переданы из React в DOM браузера.
Скажем, например, если вам понадобилось установить node.scrollHeight на обработанный node.scrollTop, тогда React DOM-элементов может быть недостаточно. Вам нужно подождать, пока элементы будут окрашены, чтобы получить их высоту.
Решение:
Используйте requestAnimationFrame
, чтобы убедиться, что ваш код запущен после рисования вашего вновь созданного объекта
scrollElement: function() {
//store a this ref, and
var _this = this;
//wait for a paint to do scrolly stuff
window.requestAnimationFrame(function() {
var node = _this.getDOMNode();
if (node !== undefined) {
//and scroll them!
node.scrollTop = node.scrollHeight;
}
});
},
componentDidMount: function() {
this.scrollElement();
},
// and or
componentDidUpdate: function() {
this.scrollElement();
},
// and or
render: function() {
this.scrollElement()
return [...]
По моему опыту window.requestAnimationFrame
было недостаточно, чтобы гарантировать, что DOM был полностью выполнен/завершен с помощью componentDidMount
. Я запускаю код, который обращается к DOM сразу после вызова componentDidMount
, и использование только window.requestAnimationFrame
приведет к тому, что элемент будет присутствовать в DOM; однако обновления размеров элементов пока не отражены, так как переплавка еще не произошла.
Единственный надежный способ для этого - обернуть мой метод в setTimeout
и window.requestAnimationFrame
, чтобы убедиться, что текущий стек вызовов будет очищен до регистрации для следующего рендеринга кадра.
function onNextFrame(callback) {
setTimeout(function () {
window.requestAnimationFrame(callback)
}, 0)
}
Если мне приходилось размышлять о том, почему это происходит/необходимо, я мог видеть, что React пакетно обновляет DOM и фактически не применяет изменения к DOM до завершения текущего стека.
В конечном счете, если вы используете измерения DOM в коде, который вы запускаете после обратных вызовов React, вы, вероятно, захотите использовать этот метод.
0
в setTimeout, если вы не даете ему время.
Я чувствую, что это решение грязно, но здесь мы идем:
componentDidMount() {
this.componentDidUpdate()
}
componentDidUpdate() {
// A whole lotta functions here, fired after every render.
}
Теперь я просто буду сидеть здесь и ждать низких голосов.
Из документа ReactDOM.render():
Если предоставляется дополнительный обратный вызов, он будет выполнен после компонент визуализируется или обновляется.
ReactDOM.render
, но не для ReactElement.render
уровня ReactElement.render
(о чем здесь идет ReactElement.render
).
У меня на самом деле проблема с подобным поведением, я визуализую элемент видео в компоненте с его атрибутом id, поэтому, когда RenderDOM.render() заканчивает, он загружает плагин, который нуждается в идентификаторе, чтобы найти местозаполнитель, и он не может найти его.
SetTimeout с 0ms внутри компонентаDidMount() исправил его:)
componentDidMount() {
if (this.props.onDidMount instanceof Function) {
setTimeout(() => {
this.props.onDidMount();
}, 0);
}
}
После рендеринга вы можете указать высоту, как показано ниже, и указать высоту для соответствующих компонентов реакции.
render: function () {
var style1 = {height: '100px'};
var style2 = { height: '100px'};
//window. height actually will get the height of the window.
var hght = $(window).height();
var style3 = {hght - (style1 + style2)} ;
return (
<div className="wrapper">
<Sidebar />
<div className="inner-wrapper">
<ActionBar style={style1} title="Title Here" />
<BalanceBar style={style2} balance={balance} />
<div className="app-content" style={style3}>
<List items={items} />
</div>
</div>
</div>
);`
}
или вы можете указать высоту каждого реагирующего компонента с помощью sass. Укажите первый 2-х активный основной элемент компонента с фиксированной шириной, а затем третий основной размер главного элемента с авто. Таким образом, на основе третьего содержимого div будет назначена высота.
React имеет несколько методов жизненного цикла, которые помогают в этих ситуациях, включая, помимо прочего, getInitialState, getDefaultProps, componentWillMount, componentDidMount и т.д.
В вашем случае и случаях, которые должны взаимодействовать с элементами DOM, вам нужно подождать, пока dom будет готов, поэтому используйте componentDidMount, как показано ниже:
/** @jsx React.DOM */
var List = require('../list');
var ActionBar = require('../action-bar');
var BalanceBar = require('../balance-bar');
var Sidebar = require('../sidebar');
var AppBase = React.createClass({
componentDidMount: function() {
ReactDOM.findDOMNode(this).height = /* whatever HEIGHT */;
},
render: function () {
return (
<div className="wrapper">
<Sidebar />
<div className="inner-wrapper">
<ActionBar title="Title Here" />
<BalanceBar balance={balance} />
<div className="app-content">
<List items={items} />
</div>
</div>
</div>
);
}
});
module.exports = AppBase;
Также для получения дополнительной информации о жизненном цикле в реакции вы можете посмотреть ссылку ниже: https://facebook.github.io/react/docs/state-and-lifecycle.html
Немного обновить с помощью ES6
классов вместо React.createClass
import React, { Component } from 'react';
class SomeComponent extends Component {
constructor(props) {
super(props);
// this code might be called when there is no element avaliable in `document` yet (eg. initial render)
}
componentDidMount() {
// this code will be always called when component is mounted in browser DOM ('after render')
}
render() {
return (
<div className="component">
Some Content
</div>
);
}
}
Также - проверьте методы жизненного цикла компонентов React: https://facebook.github.io/react/docs/react-component.html#the-component-lifecycle
Каждый компонент имеет множество методов, подобных componentDidMount
, например.
componentWillUnmount()
- компонент будет удален из браузера DOMЯ столкнулся с той же проблемой.
В большинстве сценариев, использующих hack-ish setTimeout(() => { }, 0)
в componentDidMount()
, работал.
Но не в частном случае; и я не хотел использовать ReachDOM findDOMNode
, так как в документации говорится:
Примечание: findDOMNode - это escape-люк, используемый для доступа к базовому DOM node. В большинстве случаев использование этого выходного люка не рекомендуется он пробивает абстракцию компонента.
(Источник: https://facebook.github.io/react/docs/react-dom.html#finddomnode)
Итак, в этом конкретном компоненте мне пришлось использовать событие componentDidUpdate()
, поэтому мой код оказался следующим:
componentDidMount() {
// feel this a little hacky? check this: http://stackoverflow.com/questions/26556436/react-after-render-code
setTimeout(() => {
window.addEventListener("resize", this.updateDimensions.bind(this));
this.updateDimensions();
}, 0);
}
И затем:
componentDidUpdate() {
this.updateDimensions();
}
Наконец, в моем случае мне пришлось удалить слушателя, созданного в componentDidMount
:
componentWillUnmount() {
window.removeEventListener("resize", this.updateDimensions.bind(this));
}