reactjs - props - setstate prevstate react
¿Se puede obligar a un componente React a volver a reproducir sin llamar a setState? (18)
Tengo un objeto externo (al componente) observable en el que quiero escuchar los cambios. Cuando el objeto se actualiza, emite eventos de cambio, y luego quiero volver a procesar el componente cuando se detecta cualquier cambio.
Con un
React.render
nivel
React.render
esto ha sido posible, pero dentro de un componente no funciona (lo cual tiene sentido ya que el método de
render
solo devuelve un objeto).
Aquí hay un ejemplo de código:
export default class MyComponent extends React.Component {
handleButtonClick() {
this.render();
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick.bind(this)}>
Click me
</button>
</div>
)
}
}
Al hacer clic internamente en el botón, se llama
this.render()
, pero eso no es lo que realmente hace que se produzca la representación (puede ver esto en acción porque el texto creado por
{Math.random()}
no cambia).
Sin embargo, si simplemente llamo
this.setState()
lugar de
this.render()
, funciona bien.
Entonces, supongo que mi pregunta es: ¿los componentes React deben tener un estado para volver a reproducir? ¿Hay alguna forma de obligar al componente a actualizarse bajo demanda sin cambiar el estado?
Entonces, supongo que mi pregunta es: ¿los componentes React deben tener un estado para volver a reproducir? ¿Hay alguna forma de obligar al componente a actualizarse bajo demanda sin cambiar el estado?
Las otras respuestas han tratado de ilustrar cómo podrías hacerlo, pero el punto es que no deberías . Incluso la solución hacky de cambiar la clave pierde el punto. El poder de React es ceder el control de administrar manualmente cuándo algo debería renderizarse, y en cambio solo preocuparse por cómo algo debería mapearse en las entradas. Luego suministrar flujo de entradas.
Si necesita forzar manualmente el re-renderizado, seguramente no está haciendo algo bien.
Vincula los cambios de la tienda usando HOC
Usando el patrón HOC (componente de orden superior) , puede envolver sus componentes React y tener actualizaciones automáticas cuando cambien sus tiendas. Este es un enfoque muy ligero sin un marco.
withStores HOC maneja las actualizaciones de la tienda
import React, { Component } from ''react''
export default function(/* store1, store2, store3... */) {
const storeArgs = Array.prototype.slice.call(arguments)
return function(WrappedComponent) {
return class WithStore extends Component {
constructor(props) {
super(props)
this.state = {
lastUpdated: Date.now()
}
this.stores = storeArgs
}
_onChange = () => {
this.setState({ lastUpdated: Date.now() })
}
componentWillMount = () => {
this.stores && this.stores.forEach(store => {
// each store has a common change event to subscribe to
store.on(''change'', this._onChange)
})
}
componentWillUnmount = () => {
this.stores && this.stores.forEach(store => {
store.off(''change'', this._onChange)
})
}
render() {
return <WrappedComponent
lastUpdated={this.state.lastUpdated}
{...this.props} />
}
}
}
}
Cómo utilizar
import React, { Component } from ''react''
import { View, Text, Button } from ''react-native''
import withStores from ''./withStores''
import userStore from ''./stores/users''
class MyView {
_increaseUserCount = () => {
userStore.setState({ userCount: userStore.state.count + 1 })
}
render() {
return (
<View>
<Text>User count: {userStore.state.count}</Text>
<Button
title="Increase User Count"
onPress={this._increaseUserCount}
/>
</View>
)
}
}
return withStores(userStore)(MyView)
Ejemplo de clase de tienda simple
var ee = require(''event-emitter'')
export default class Store {
constructor() {
this._state = {}
this._eventEmitter = ee({})
}
get state() {
return this._state
}
setState(newState) {
this._state = {...this._state, ...newState}
this._eventEmitter.emit(''change'')
}
on(ev, fn) {
this._eventEmitter.on(ev, fn)
}
off(ev, fn) {
this._eventEmitter.off(ev, fn)
}
}
Almacenar instancia como singleton
import Store from ''./Store''
const myStore = new Store()
export default myStore
Esto realmente está destinado a proyectos más pequeños con árboles de baja profundidad de componentes. La razón es que si usa esto para un árbol de componentes más grande, esto no es muy selectivo sobre qué partes actualizar para una tienda, solo la tienda en sí activa la actualización.
Estará bien si divide sus tiendas de una manera más granular, pero una vez más, esta es la razón por la cual redux es bueno, ya que puede ser granular según sea necesario y solo requiere una sola tienda, pero también tiene un diseño desproporcionado en un proyecto más pequeño.
forceUpdate
haciendo lo siguiente
CAMINO INCORRECTO : no utilice el índice como clave
this.state.rows.map((item, index) =>
<MyComponent cell={item} key={index} />
)
MANERA CORRECTA : Use la identificación de datos como clave, puede ser una guía, etc.
this.state.rows.map((item) =>
<MyComponent item={item} key={item.id} />
)
así que al hacer tal mejora de código, su componente será ÚNICO y se renderizará naturalmente
Cuando desee que se comuniquen dos componentes React, que no están vinculados por una relación (padre-hijo), es recomendable utilizar Flux o arquitecturas similares.
Lo que desea hacer es escuchar los cambios del almacén de
componentes observables
, que contiene el modelo y su interfaz, y guardar los datos que provocan que el render cambie como
state
en
MyComponent
.
Cuando la tienda envía los nuevos datos, cambia el estado de su componente, lo que activa automáticamente el renderizado.
Normalmente debe intentar evitar usar
forceUpdate()
.
De la documentación:
Normalmente, debe intentar evitar todos los usos de forceUpdate () y solo leer de this.props y this.state en render (). Esto hace que su aplicación sea mucho más simple y eficiente
ES6 - Incluyo un ejemplo que me fue útil:
En una "instrucción if corta" puede pasar una función vacía como esta:
isReady ? ()=>{} : onClick
Este parece ser el enfoque más corto.
()=>{}
En realidad,
forceUpdate()
es la única solución correcta ya que
setState()
podría
no
desencadenar una nueva representación si se implementa una lógica adicional en
shouldComponentUpdate()
o cuando simplemente devuelve
false
.
forceUpdate ()
Llamar a
forceUpdate()
hará que seforceUpdate()
render()
en el componente, omitiendoshouldComponentUpdate()
. forceUpdate()
setState ()
setState()
siempre activará una nueva representación a menos que se implemente una lógica de representación condicional enshouldComponentUpdate()
. more...
forceUpdate()
puede
forceUpdate()
desde su componente mediante
this.forceUpdate()
En su componente, puede llamar a
this.forceUpdate()
para forzar una repetición.
Documentación: https://facebook.github.io/react/docs/component-api.html
Hay algunas formas de volver a procesar su componente:
La solución más simple es usar el método forceUpdate ():
this.forceUpdate()
Una solución más es crear una clave no utilizada en el estado (nonUsedKey) y llamar a la función setState con la actualización de esta nonUsedKey:
this.setState({ nonUsedKey: Date.now() } );
O reescribir todo el estado actual:
this.setState(this.state);
El cambio de utilería también proporciona la recuperación de componentes.
He encontrado que es mejor evitar forceUpdate (). Una forma de forzar el re-render es agregar dependencia de render () en una variable externa temporal y cambiar el valor de esa variable cuando sea necesario.
Aquí hay un ejemplo de código:
class Example extends Component{
constructor(props){
this.state = {temp:0};
this.forceChange = this.forceChange.bind(this);
}
forceChange(){
this.setState(prevState => ({
temp: prevState.temp++
}));
}
render(){
return(
<div>{this.state.temp &&
<div>
... add code here ...
</div>}
</div>
)
}
}
Llame this.forceChange () cuando necesite forzar el renderizado.
Otra forma es llamar a
setState
,
Y
preservar el estado:
this.setState(prevState=>({...prevState}));
Para lograr lo que está describiendo, intente this.forceUpdate ().
Podemos usar this.forceUpdate () como se muestra a continuación.
class MyComponent extends React.Component {
handleButtonClick = ()=>{
this.forceUpdate();
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick}>
Click me
</button>
</div>
)
}
}
ReactDOM.render(<MyComponent /> , mountNode);
La parte Elemento ''Math.random'' en el DOM solo se actualiza incluso si usa setState para volver a representar el componente.
Todas las respuestas aquí son correctas, complementando la pregunta para la comprensión ... ya que sabemos que volver a representar un componente sin usar setState ({}) es usando forceUpdate ().
El código anterior se ejecuta con setState como se muestra a continuación.
class MyComponent extends React.Component {
handleButtonClick = ()=>{
this.setState({ });
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick}>
Click me
</button>
</div>
)
}
}
ReactDOM.render(<MyComponent /> , mountNode);
Podrías hacerlo de dos maneras:
1. Use el método
forceUpdate()
:
Hay algunos problemas
forceUpdate()
que pueden ocurrir al usar el método
forceUpdate()
.
Un ejemplo es que ignora el método
shouldComponentUpdate()
y volverá a representar la vista independientemente de si
shouldComponentUpdate()
devuelve falso.
Debido a esto, se debe evitar usar forceUpdate () cuando sea posible.
2. Pasar this.state al método
setState()
La siguiente línea de código supera el problema con el ejemplo anterior:
this.setState(this.state);
Realmente todo lo que está haciendo es sobrescribir el estado actual con el estado actual que desencadena una nueva representación. Aún así, esta no es necesariamente la mejor manera de hacer las cosas, pero supera algunos de los problemas técnicos que puede encontrar utilizando el método forceUpdate ().
Puede usar forceUpdate () para ver más detalles ( forceUpdate() ).
Solo otra respuesta para respaldar la respuesta aceptada :-)
React desalienta el uso de
forceUpdate()
porque generalmente tienen un enfoque muy "esta es la única forma de hacerlo" hacia la programación funcional.
Esto está bien en muchos casos, pero muchos desarrolladores de React vienen con un fondo OO, y con ese enfoque, está perfectamente bien escuchar un objeto observable.
Y si lo hace, probablemente sepa que DEBE volver a renderizar cuando el "fuego" observable, y como tal, DEBERÍA usar
forceUpdate()
y en realidad es un plus que
shouldComponentUpdate()
NO está involucrado aquí.
Herramientas como MobX, que adopta un enfoque OO, en realidad está haciendo esto debajo de la superficie (en realidad, MobX llama
render()
directamente)
forceUpdate (), pero cada vez que escuché a alguien hablar sobre eso, se le ha seguido y nunca debe usar esto.
forceUpdate();
el método funcionará pero es recomendable usar
setState();
forceUpdate
debe evitarse porque se desvía de una mentalidad React.
Los documentos de React citan un ejemplo de cuándo
forceUpdate
podría usarse:
Por defecto, cuando el estado o los accesorios de su componente cambian, su componente se volverá a representar. Sin embargo, si estos cambian implícitamente (por ejemplo: los datos profundos dentro de un objeto cambian sin cambiar el objeto mismo) o si su método render () depende de otros datos, puede decirle a React que necesita volver a ejecutar render () llamando forceUpdate ().
Sin embargo, me gustaría proponer la idea de que incluso con objetos profundamente anidados,
forceUpdate
es innecesario.
Al usar una fuente de datos inmutable, los cambios de seguimiento se vuelven baratos;
un cambio siempre dará como resultado un nuevo objeto, por lo que solo necesitamos verificar si la referencia al objeto ha cambiado.
Puede usar la biblioteca
Immutable JS
para implementar objetos de datos inmutables en su aplicación.
Normalmente, debe intentar evitar todos los usos de forceUpdate () y solo leer de this.props y this.state en render (). Esto hace que su componente sea "puro" y que su aplicación sea mucho más simple y eficiente. forceUpdate()
Cambiar la clave del elemento que desea volver a representar funcionará. Establezca la clave de apoyo en su elemento a través del estado y luego cuando desee actualizar el estado establecido para tener una nueva clave.
<Element key={this.state.key} />
Luego se produce un cambio y restablece la clave
this.setState({ key: Math.random() });
Quiero señalar que esto reemplazará el elemento en el que está cambiando la clave. Un ejemplo de dónde esto podría ser útil es cuando tiene un campo de entrada de archivo que desea restablecer después de cargar una imagen.
Si bien la verdadera respuesta a la pregunta del OP sería
forceUpdate()
, he encontrado esta solución útil en diferentes situaciones.
También quiero señalar que si te encuentras usando
forceUpdate
es posible que desees revisar tu código y ver si hay otra forma de hacer las cosas.
NOTA 1-9-2019:
Lo anterior (cambiando la clave) reemplazará completamente el elemento.
Si te encuentras actualizando la clave para que los cambios sucedan, probablemente tengas un problema en otro lugar de tu código.
Usar
Math.random()
en clave volverá a crear el elemento con cada render.
NO recomendaría actualizar la clave de esta manera, ya que react utiliza la clave para determinar la mejor manera de volver a representar las cosas.