javascript - instalar - react js installation
¿Reacciona el código "después de renderizar"? (14)
Tengo una aplicación donde necesito establecer la altura de un elemento (digamos "contenido de la aplicación") dinámicamente. Toma la altura del "cromo" de la aplicación y la resta y luego establece la altura del "contenido de la aplicación" para que se ajuste al 100% dentro de esas restricciones. Esto es súper simple con las vistas vanilla JS, jQuery o Backbone, pero estoy luchando por descubrir cuál sería el proceso correcto para hacer esto en React.
A continuación se muestra un componente de ejemplo.
Quiero poder configurar la altura del
app-content
para que sea el 100% de la ventana menos el tamaño de
ActionBar
y
BalanceBar
, pero ¿cómo sé cuándo se representa todo y dónde colocaría el material de cálculo en esta clase de reacción? ?
/** @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;
De la documentación de ReactDOM.render() :
Si se proporciona la devolución de llamada opcional, se ejecutará después de que el componente se procese o actualice.
Después de renderizar, puede especificar la altura como se muestra a continuación y puede especificar la altura a los componentes de reacción correspondientes.
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>
);`
}
o puede especificar la altura de cada componente de reacción usando sass. Especifique los primeros 2 componentes principales del componente de reacción con ancho fijo y luego la altura del tercer componente principal del componente con auto. Entonces, según el contenido del tercer div, se asignará la altura.
En mi experiencia,
window.requestAnimationFrame
no fue suficiente para garantizar que el DOM se haya procesado completamente / reflujo completo desde
componentDidMount
.
Tengo un código en ejecución que accede al DOM inmediatamente después de una llamada
componentDidMount
y que usa únicamente
window.requestAnimationFrame
daría como resultado que el elemento esté presente en el DOM;
sin embargo, las actualizaciones de las dimensiones del elemento aún no se reflejan ya que aún no se ha producido un reflujo.
La única forma verdaderamente confiable para que esto funcionara fue envolver mi método en un
setTimeout
y una
window.requestAnimationFrame
para garantizar que la pila de llamadas actual de React se
window.requestAnimationFrame
antes de registrarse para el procesamiento del siguiente marco.
function onNextFrame(callback) {
setTimeout(function () {
requestAnimationFrame(callback)
})
}
Si tuviera que especular sobre por qué ocurre esto / es necesario, podría ver Reaccionar actualizaciones de DOM por lotes y no aplicar los cambios al DOM hasta que se complete la pila actual.
En última instancia, si está utilizando mediciones DOM en el código que está disparando después de las devoluciones de llamada React, es probable que desee utilizar este método.
En realidad, estoy teniendo problemas con un comportamiento similar, renderizo un elemento de video en un Componente con su atributo de identificación, por lo que cuando RenderDOM.render () finaliza, carga un complemento que necesita la identificación para encontrar el marcador de posición y no lo encuentra.
El setTimeout con 0ms dentro del componenteDidMount () lo arregló :)
componentDidMount() {
if (this.props.onDidMount instanceof Function) {
setTimeout(() => {
this.props.onDidMount();
}, 0);
}
}
Para mí, ninguna combinación de
window.requestAnimationFrame
o
setTimeout
produjo resultados consistentes.
A veces funcionó, pero no siempre, o a veces sería demasiado tarde.
Lo arreglé haciendo loop
window.requestAnimationFrame
tantas veces como sea necesario.
(Típicamente 0 o 2-3 veces)
La clave es
diff > 0
: aquí podemos asegurarnos exactamente cuando la página se actualice.
// Ensure new image was loaded before scrolling
if (oldH > 0 && images.length > prevState.images.length) {
(function scroll() {
const newH = ref.scrollHeight;
const diff = newH - oldH;
if (diff > 0) {
const newPos = top + diff;
window.scrollTo(0, newPos);
} else {
window.requestAnimationFrame(scroll);
}
}());
}
Puede cambiar el estado y luego hacer sus cálculos en la devolución de llamada setState . Según la documentación de React, "se garantiza que se activará después de que se haya aplicado la actualización".
Esto debe hacerse en
componentDidMount
o en otro lugar del código (como en un controlador de eventos de cambio de tamaño) en lugar de hacerlo en el constructor.
Esta es una buena alternativa a
window.requestAnimationFrame
y no tiene los problemas que algunos usuarios han mencionado aquí (es necesario combinarlo con
setTimeout
o llamarlo varias veces).
Por ejemplo:
class AppBase extends React.Component {
state = {
showInProcess: false,
size: null
};
componentDidMount() {
this.setState({ showInProcess: true }, () => {
this.setState({
showInProcess: false,
size: this.calculateSize()
});
});
}
render() {
const appStyle = this.state.showInProcess ? { visibility: ''hidden'' } : null;
return (
<div className="wrapper">
...
<div className="app-content" style={appStyle}>
<List items={items} />
</div>
...
</div>
);
}
}
React tiene pocos métodos de ciclo de vida que ayudan en estas situaciones, las listas incluyen, entre otras, getInitialState, getDefaultProps, componentWillMount, componentDidMount, etc.
En su caso y los casos que deben interactuar con los elementos DOM, debe esperar hasta que el dom esté listo, por lo tanto, use componentDidMount como se muestra a continuación:
/** @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;
También para obtener más información sobre el ciclo de vida en reaccionar, puede consultar el siguiente enlace: https://facebook.github.io/react/docs/state-and-lifecycle.html
Siento que esta solución está sucia, pero aquí vamos:
componentDidMount() {
this.componentDidUpdate()
}
componentDidUpdate() {
// A whole lotta functions here, fired after every render.
}
Ahora me voy a sentar aquí y esperar los votos negativos.
Solo para actualizar un poco esta pregunta con los nuevos métodos de gancho, simplemente puede usar el gancho
useEffect
:
import React, { useEffect } from ''react''
export default function App(props) {
useEffect(() => {
// your post layout code (or ''effect'') here.
...
},
// array of variables that can trigger an update if they change. Pass an
// an empty array if you just want to run it once after component mounted.
[])
}
Además, si desea ejecutar antes del montaje de diseño, use el
useLayoutEffect
:
import React, { useLayoutEffect } from ''react''
export default function App(props) {
useLayoutEffect(() => {
// your pre layout code (or ''effect'') here.
...
}, [])
}
Tuve el mismo problema.
En la mayoría de los escenarios, el uso de hack-ish
setTimeout(() => { }, 0)
en
componentDidMount()
funcionó.
Pero no en un caso especial;
y no quería usar
ReachDOM findDOMNode
ya que la documentación dice:
Nota: findDOMNode es una trampilla de escape utilizada para acceder al nodo DOM subyacente. En la mayoría de los casos, se desaconseja el uso de esta trampilla de escape porque perfora la abstracción del componente.
(Fuente: findDOMNode )
Entonces, en ese componente en particular, tuve que usar el evento
componentDidUpdate()
, por lo que mi código terminó siendo así:
componentDidMount() {
// feel this a little hacky? check this: http://.com/questions/26556436/react-after-render-code
setTimeout(() => {
window.addEventListener("resize", this.updateDimensions.bind(this));
this.updateDimensions();
}, 0);
}
Y entonces:
componentDidUpdate() {
this.updateDimensions();
}
Finalmente, en mi caso, tuve que eliminar el oyente creado en
componentDidMount
:
componentWillUnmount() {
window.removeEventListener("resize", this.updateDimensions.bind(this));
}
Tuve una situación extraña cuando necesito imprimir un componente de reacción que recibe una gran cantidad de datos y pintar en el lienzo. He probado todos los enfoques mencionados, ninguno de ellos funcionó de manera confiable para mí, con requestAnimationFrame dentro de setTimeout obtengo un lienzo vacío en el 20% del tiempo, así que hice lo siguiente:
nRequest = n => range(0,n).reduce(
(acc,val) => () => requestAnimationFrame(acc), () => requestAnimationFrame(this.save)
);
Básicamente hice una cadena de requestAnimationFrame''s, no estoy seguro de si es una buena idea o no, pero hasta ahora funciona en el 100% de los casos (estoy usando 30 como valor para n variable).
Un poco de actualización con las clases de
ES6
lugar de
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>
);
}
}
Además, verifique los métodos del ciclo de vida del componente React: el ciclo de vida del componente
Cada componente tiene muchos métodos similares a
componentDidMount
por ejemplo.
-
componentWillUnmount()
: el componente está a punto de eliminarse del DOM del navegador
Una desventaja de usar
componentDidUpdate
, o
componentDidMount
es que en realidad se ejecutan antes de que se dibujen los elementos dom, pero después de que se hayan pasado de React al DOM del navegador.
Digamos, por ejemplo, si necesita establecer node.scrollHeight en el node.scrollTop representado, entonces los elementos DOM de React pueden no ser suficientes. Debe esperar hasta que los elementos terminen de pintarse para obtener su altura.
Solución:
Use
requestAnimationFrame
para asegurarse de que su código se ejecute después de pintar su objeto recién renderizado
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 [...]
Este método se llama una vez después de que se representa su componente. Entonces su código se vería así.
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>
);
}
});