javascript reactjs create-react-app

javascript - ReactJS: elevar el estado frente a mantener un estado local



create-react-app (3)

En mi empresa, estamos migrando el front-end de una aplicación web a ReactJS. Estamos trabajando con create-react-app (actualizado a v16), sin Redux. Ahora estoy atascado en una página cuya estructura se puede simplificar con la siguiente imagen:

Los datos que muestran los tres componentes (SearchableList, SelectableList y Map) se recuperan con la misma solicitud de back-end en el método componentDidMount() de MainContainer. El resultado de esta solicitud se almacena en el estado de MainContainer y tiene una estructura más o menos así:

state.allData = { left: { data: [ ... ] }, right: { data: [ ... ], pins: [ ... ] } }

LeftContainer recibe como prop state.allData.left de MainContainer y pasa props.left.data a SearchableList, una vez más como prop.

RightContainer recibe como prop state.allData.right de MainContainer y pasa props.right.data a props.right.pins y props.right.pins a Map.

SelectableList muestra una casilla de verificación para permitir acciones en sus elementos. Siempre que ocurra una acción en un elemento del componente SelectableList, puede tener efectos secundarios en los pines del mapa.

He decidido almacenar en el estado de RightContainer una lista que mantiene todos los identificadores de los elementos mostrados por SelectableList; esta lista se pasa como accesorios a SelectableList y Map. Luego paso a SelectableList una devolución de llamada, que cada vez que se realiza una selección actualiza la lista de identificadores dentro de RightContainer; los nuevos accesorios llegan tanto a SelectableList como a Map, por lo render() se llama a render() en ambos componentes.

Funciona bien y ayuda a mantener todo lo que le puede suceder a SelectableList y Map dentro de RightContainer, pero estoy preguntando si esto es correcto para los conceptos de elevación de estado y fuente única de verdad .

Como alternativa factible, pensé en agregar una propiedad _selected a cada elemento en state.right.data en MainContainer y pasar la devolución de llamada de selección tres niveles a SelectableList, manejando todas las acciones posibles en MainContainer. Pero tan pronto como ocurra un evento de selección, esto eventualmente forzará la carga de LeftContainer y RightContainer, introduciendo la necesidad de implementar lógicas como shouldComponentUpdate() para evitar el render() inútil render() especialmente en LeftContainer.

¿Cuál es / podría ser la mejor solución para optimizar esta página desde un punto de vista arquitectónico y de rendimiento?

A continuación tiene un extracto de mis componentes para ayudarlo a comprender la situación.

MainContainer.js

class MainContainer extends React.Component { constructor(props) { super(props); this.state = { allData: {} }; } componentDidMount() { fetch( ... ) .then((res) => { this.setState({ allData: res }); }); } render() { return ( <div className="main-container"> <LeftContainer left={state.allData.left} /> <RightContainer right={state.allData.right} /> </div> ); } } export default MainContainer;

RightContainer.js

class RightContainer extends React.Component { constructor(props) { super(props); this.state = { selectedItems: [ ... ] }; } onDataSelection(e) { const itemId = e.target.id; // ... handle itemId and selectedItems ... } render() { return ( <div className="main-container"> <SelectableList data={props.right.data} onDataSelection={e => this.onDataSelection(e)} selectedItems={this.state.selectedItems} /> <Map pins={props.right.pins} selectedItems={this.state.selectedItems} /> </div> ); } } export default RightContainer;

¡Gracias por adelantado!


Al usar Redux, puede evitar tales devoluciones de llamada y mantener todo el estado en una sola tienda, así que haga que su componente principal esté conectado, y que los componentes izquierdo y derecho sean tontos, y simplemente pase los accesorios que obtiene de padres a hijos, y usted No tiene que preocuparse por las devoluciones de llamada en este caso.


Como dice React docs

A menudo, varios componentes necesitan reflejar los mismos datos cambiantes. Recomendamos elevar el estado compartido hasta su ancestro común más cercano.

Debe haber una única "fuente de verdad" para cualquier dato que cambie en una aplicación React. Por lo general, el estado se agrega primero al componente que lo necesita para la representación. Luego, si otros componentes también lo necesitan, puede elevarlo hasta su ancestro común más cercano. En lugar de intentar sincronizar el estado entre diferentes componentes, debe confiar en el flujo de datos de arriba hacia abajo.

El estado de elevación implica escribir más código "repetitivo" que los enfoques de enlace bidireccionales, pero como beneficio, requiere menos trabajo encontrar y aislar errores. Dado que cualquier estado "vive" en algún componente y ese componente solo puede cambiarlo, la superficie de los insectos se reduce considerablemente. Además, puede implementar cualquier lógica personalizada para rechazar o transformar la entrada del usuario.

Esencialmente, también necesita elevar los estados del árbol que se están utilizando en el componente Hermanos también. Por lo tanto, su primera implementación donde almacena los RightContainer selectedItems como un estado en RightContainer está completamente justificada y es un buen enfoque, ya que el padre no necesita saber y estos data están siendo compartidos por los dos componentes child de RightContainer y esos dos ahora. tener una sola fuente de verdad.

Según su pregunta:

Como alternativa factible, pensé en agregar una propiedad _selected a cada elemento en state.right.data en MainContainer y pasar la devolución de llamada de selección tres niveles hacia abajo a SelectableList , manejando todas las acciones posibles en MainContainer

No estaría de acuerdo con que este es un enfoque mejor que el primero, ya que MainContainer no necesita conocer los MainContainer selectedItems o el controlador de ninguna de las actualizaciones. MainContainer no está haciendo nada sobre esos estados y solo lo está transmitiendo.

Considere optimise on performance , usted mismo habla sobre la implementación de shouldComponentUpdate , pero puede evitarlo creando sus componentes extendiendo React.PureComponent que esencialmente implementa el shouldComponentUpdate con una comparación shallow de state y props .

Según los documentos:

Si la función render() su componente React rinde el mismo resultado con los mismos accesorios y estado, puede usar React.PureComponen t para aumentar el rendimiento en algunos casos.

Sin embargo, si varios componentes profundamente anidados están utilizando los mismos datos, tiene sentido utilizar redux y almacenar esos datos en el estado redux. De esta forma, es accesible globalmente para toda la aplicación y se puede compartir entre componentes que no están directamente relacionados.

Por ejemplo, considere el siguiente caso

const App = () => { <Router> <Route path="/" component={Home}/> <Route path="/mypage" component={MyComp}/> </Router> }

Ahora aquí si tanto Home como MyComp desean acceder a los mismos datos. Puede pasar los datos como accesorios desde la aplicación llamándolos a través de render prop. Sin embargo, se haría fácilmente conectando ambos componentes al estado de Redux usando una función de connect como

const mapStateToProps = (state) => { return { data: state.data } } export connect(mapStateToProps)(Home);

y de manera similar para MyComp . También es fácil de configurar acciones para actualizar información relevante

Además, es particularmente fácil de configurar Redux para su aplicación y podrá almacenar datos relacionados con las mismas cosas en los reductores individuales. De esta manera, también podrá modularizar los datos de su aplicación


Mi honesto consejo sobre esto. De la experiencia es:

Redux es simple. Es fácil de entender y escalar, PERO debe usar Redux para algunos casos de uso específicos.

Como Redux encapsula su aplicación, puede pensar en almacenar cosas como:

  • configuración regional de la aplicación actual
  • usuario autenticado actual
  • token actual de alguna parte

Cosas que necesitarías a escala global. react-redux incluso permite un decorador @connect en componentes. Asi como:

@connect(state => ({ locale: state.locale, currentUser: state.currentUser })) class App extends React.Component

Todos ellos se transmiten como accesorios y connect se pueden usar en cualquier lugar de la aplicación. Aunque recomiendo simplemente pasar los accesorios globales con el operador de propagación

<Navbar {...this.props} />

Todos los demás componentes (o "páginas") dentro de su aplicación pueden hacer su propio estado encapsulado. Por ejemplo, la página Usuarios puede hacer lo suyo.

class Users extends React.Component { constructor(props) { super(props); this.state = { loadingUsers: false, users: [], }; } ......

Accedería a locale y currentUser a través de accesorios porque se transmitieron desde los componentes del Contenedor.

Este enfoque lo he hecho varias veces y funciona.

Pero , dado que quería consolidar realmente el conocimiento de React primero, antes de hacer Redux puede simplemente almacenar su estado en el componente de nivel superior y transmitirlo a los niños.

Desventajas:

  • Tendrás que seguir pasándolos a componentes de nivel interno
  • Para actualizar el estado desde los componentes de nivel interno, tendrá que pasar la función que actualiza el estado.

Estas desventajas son un poco aburridas y difíciles de manejar. Por eso se construyó Redux.

Espero haber ayudado. buena suerte