Pregunta relacionada aquí , pero no estoy seguro de cómo adaptar la solución a este problema.
Estoy tratando de crear un componente reutilizable para una página de destino con pestañas. Cada pestaña es un elemento secundario del componente reutilizable y tiene su propia tienda definida como accesorio:
<LandingPage> <LandingPage.Tab store={store1}/> <LandingPage.Tab store={store2}/> ... <LandingPage.Tab store={storeN}/> </LandingPage>Me gustaría obtener datos de la tienda de cada pestaña cuando se monta el componente principal para permitir un cambio rápido entre pestañas. Dentro de la función componentDidMount, itero sobre cada elemento secundario y asigno la devolución de llamada onChange para la tienda del elemento secundario a una función de flecha anónima:
var LandingPage = React.createClass({ getInitialState: function () { return { data: [] /* each index will be an array of data for a different tab */ }; }, componentDidMount: function () { var self = this; React.Children.forEach(this.props.children, function (child, index) { child.props.store.onChange(() => { self.setDataAtIndex(index, child.props.store.getData()); }); }); }, setDataAtIndex: function (index, newData) { var data = this.state.data.slice(); data[index] = newData; this.setState({ data: data }); }, ... });Sin embargo, cuando la página se carga por primera vez, recibo un mensaje de advertencia de React:
Advertencia: setState (...): solo puede actualizar un componente montado o de montaje. Esto generalmente significa que llamó a setState() en un componente desmontado. Esto no es operativo. Compruebe el código del componente LandingPage.
Estoy confundido porque pensé que podría asumir que el componente está montado si estoy dentro de la función componentDidMount. Este mensaje de advertencia desaparece cuando actualizo la página.
¿Alguien puede explicarme este comportamiento y decirme cómo estructurar correctamente el código para eliminar el mensaje de advertencia?
Esta función...
() => { // this calls setState self.setDataAtIndex(index, child.props.store.getData()); } Se llamará cada vez que una tienda de pestañas cambie si el componente LandingPage está montado o no. Eso es un problema. Debe indicar a las tiendas que dejen de llamar a esta función cuando se desmonte LandingPage . Sin modificar la tienda, podría anular el detector de cambios con un no-op, como este...
componentWillUnmount: function () { var self = this; React.Children.forEach(this.props.children, function (child, index) { child.props.store.onChange(() => {}); }); } Ahora, cuando el componente no está montado, se debe llamar a () => {} , que no llama a setState y, por lo tanto, es inofensivo
Si bien la respuesta de Charlie Martin es una solución inteligente, finalmente decidí almacenar las devoluciones de llamada en una variable de estado, similar a la pregunta relacionada a la que me vinculé. Aquí está el código actualizado:
var LandingPage = React.createClass({ getInitialState: function () { return { data: [] /* each index will be an array of data for a different tab */ callbacks: [] /* each index will store a callback reference */ }; }, componentDidMount: function () { var self = this; React.Children.forEach(this.props.children, function (child, index) { var fn = function() { self.setDataAtIndex(index, child.props.store.getAll()); }; child.props.store.onChange(fn); self.saveCallback(index, fn); }); }, componentWillUnmount: function () { var self = this; React.Children.forEach(this.props.children, function (child, index) { child.props.store.offChange(self.state.callbacks[index]); }); }, saveCallback: function (index, fn) { var callbacks = this.state.callbacks; callbacks[index] = fn; this.setState({ callbacks: callbacks }); }, ... });Como referencia, onChange() y offChange() se definen como:
function onChange(callback) { this.on('change', callback); } function offChange(callback) { this.removeListener('change', callback); }