javascript - props - react-router-redux
Comprender React-Redux y mapStateToProps() (7)
Estoy tratando de entender el método de conexión de react-redux y las funciones que toma como parámetros.
En particular
mapStateToProps()
.
Según tengo entendido, el valor de retorno de
mapStateToProps
será un objeto derivado del estado (como vive en la tienda), cuyas claves se pasarán a su componente de destino (se aplica la conexión del componente) como accesorios.
Esto significa que el estado consumido por su componente de destino puede tener una estructura muy diferente del estado que se almacena en su tienda.
P: ¿Está bien?
P: ¿Se espera esto?
P: ¿Es esto un antipatrón?
Aquí hay un esquema / plantilla para describir el comportamiento de
mapStateToProps
:
(Esta es una implementación muy simplificada de lo que hace un contenedor Redux).
class MyComponentContainer extends Component {
mapStateToProps(state) {
// this function is specific to this particular container
return state.foo.bar;
}
render() {
// This is how you get the current state from Redux,
// and would be identical, no mater what mapStateToProps does
const { state } = this.context.store.getState();
const props = this.mapStateToProps(state);
return <MyComponent {...this.props} {...props} />;
}
}
y después
function buildReduxContainer(ChildComponentClass, mapStateToProps) {
return class Container extends Component {
render() {
const { state } = this.context.store.getState();
const props = mapStateToProps(state);
return <ChildComponentClass {...this.props} {...props} />;
}
}
}
Este ejemplo de react y redux se basa en el ejemplo de Mohamed Mellouki. Pero valida usando reglas de prettify y linting . Tenga en cuenta que definimos nuestros accesorios y métodos de dispatch utilizando PropTypes para que nuestro compilador no nos grite. Este ejemplo también incluía algunas líneas de código que faltaban en el ejemplo de Mohamed. Para usar connect necesitará importarlo desde redux . Este ejemplo también binds el método filterItems, esto evitará problemas de scope en el component . Este código fuente se ha formateado automáticamente con JavaScript prettify .
import React, { Component } from ''react-native'';
import { connect } from ''react-redux'';
import PropTypes from ''prop-types'';
class ItemsContainer extends Component {
constructor(props) {
super(props);
const { items, filters } = props;
this.state = {
items,
filteredItems: filterItems(items, filters),
};
this.filterItems = this.filterItems.bind(this);
}
componentWillReceiveProps(nextProps) {
const { itmes } = this.state;
const { filters } = nextProps;
this.setState({ filteredItems: filterItems(items, filters) });
}
filterItems = (items, filters) => {
/* return filtered list */
};
render() {
return <View>/*display the filtered items */</View>;
}
}
/*
define dispatch methods in propTypes so that they are validated.
*/
ItemsContainer.propTypes = {
items: PropTypes.array.isRequired,
filters: PropTypes.array.isRequired,
onMyAction: PropTypes.func.isRequired,
};
/*
map state to props
*/
const mapStateToProps = state => ({
items: state.App.Items.List,
filters: state.App.Items.Filters,
});
/*
connect dispatch to props so that you can call the methods from the active props scope.
The defined method `onMyAction` can be called in the scope of the componets props.
*/
const mapDispatchToProps = dispatch => ({
onMyAction: value => {
dispatch(() => console.log(`${value}`));
},
});
/* clean way of setting up the connect. */
export default connect(mapStateToProps, mapDispatchToProps)(ItemsContainer);
Este código de ejemplo es una buena plantilla como punto de partida para su componente.
P:
Is this ok?
A: si
P:
Is this expected?
Sí, esto se espera (si está utilizando react-redux).
P:
Is this an anti-pattern?
A: No, esto no es un antipatrón.
Se llama "conectar" su componente o "hacerlo inteligente". Es por diseño.
Le permite desacoplar su componente de su estado una vez más, lo que aumenta la modularidad de su código. También le permite simplificar el estado de su componente como un subconjunto del estado de su aplicación que, de hecho, lo ayuda a cumplir con el patrón de Redux.
Piénselo de esta manera: se supone que una tienda debe contener
todo el
estado de su aplicación.
Para aplicaciones grandes, esto podría contener docenas de propiedades anidadas en muchas capas de profundidad.
No desea transportar todo eso en cada llamada (caro).
Sin
mapStateToProps
o algún análogo de los mismos, estaría tentado a dividir su estado de otra manera para mejorar el rendimiento / simplificar.
Si es correcto. Es solo una función auxiliar para tener una forma más simple de acceder a sus propiedades de estado
Imagine que tiene una clave de
posts
en el estado de su aplicación
state.posts
state.posts //
/*
{
currentPostId: "",
isFetching: false,
allPosts: {}
}
*/
Y
Posts
componentes
Por defecto,
connect()(Posts)
hará que todos los accesorios de estado estén disponibles para el Componente conectado
const Posts = ({posts}) => (
<div>
{/* access posts.isFetching, access posts.allPosts */}
</div>
)
Ahora, cuando
state.posts
el estado
state.posts
a su componente, se vuelve un poco más agradable
const Posts = ({isFetching, allPosts}) => (
<div>
{/* access isFetching, allPosts directly */}
</div>
)
connect(
state => state.posts
)(Posts)
mapDispatchToProps
normalmente tiene que escribir
dispatch(anActionCreator())
con
bindActionCreators
puedes hacerlo también más fácilmente
connect(
state => state.posts,
dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)
)(Posts)
Ahora puedes usarlo en tu Componente
const Posts = ({isFetching, allPosts, fetchPosts, deletePost }) => (
<div>
<button onClick={() => fetchPosts()} />Fetch posts</button>
{/* access isFetching, allPosts directly */}
</div>
)
Actualización sobre actionCreators ..
Un ejemplo de actionCreator:
deletePost
const deletePostAction = (id) => ({
action: ''DELETE_POST'',
payload: { id },
})
Entonces,
bindActionCreators
solo tomará sus acciones, las envolverá en una llamada de
dispatch
.
(No leí el código fuente de redux, pero la implementación podría verse así:
const bindActionCreators = (actions, dispatch) => {
return Object.keys(actions).reduce(actionsMap, actionNameInProps => {
actionsMap[actionNameInProps] = (...args) => dispatch(actions[actionNameInProps].call(null, ...args))
return actionsMap;
}, {})
}
Tienes la primera parte correcta:
Sí,
mapStateToProps
tiene el estado Store como argumento / parámetro (proporcionado por
react-redux::connect
) y se usa para vincular el componente con cierta parte del estado store.
Al vincular me refiero al objeto devuelto por
mapStateToProps
se proporcionará en el momento de la construcción como accesorios y cualquier cambio posterior estará disponible a través de
componentWillReceiveProps
.
Si conoce el patrón de diseño de Observer, es exactamente eso o una pequeña variación del mismo.
Un ejemplo ayudaría a aclarar las cosas:
import React, {
Component,
} from ''react-native'';
class ItemsContainer extends Component {
constructor(props) {
super(props);
this.state = {
items: props.items, //provided by connect@mapStateToProps
filteredItems: this.filterItems(props.items, props.filters),
};
}
componentWillReceiveProps(nextProps) {
this.setState({
filteredItems: this.filterItems(this.state.items, nextProps.filters),
});
}
filterItems = (items, filters) => { /* return filtered list */ }
render() {
return (
<View>
// display the filtered items
</View>
);
}
}
module.exports = connect(
//mapStateToProps,
(state) => ({
items: state.App.Items.List,
filters: state.App.Items.Filters,
//the State.App & state.App.Items.List/Filters are reducers used as an example.
})
// mapDispatchToProps, that''s another subject
)(ItemsContainer);
Puede haber otro componente de reacción llamado
itemsFilters
que maneja la pantalla y persiste el estado del filtro en el estado de Redux Store, el componente de demostración está "escuchando" o "suscrito" a los filtros de estado de Redux Store, por lo que cada vez que los filtros almacenan cambios de estado (con la ayuda de los
filtersComponent
) react-redux detecta que hubo un cambio y notifica o "publica" todos los componentes de escucha / suscritos enviando los cambios a su
componentWillReceiveProps
que, en este ejemplo, activará un refiltro de los elementos y actualizará la pantalla debido al hecho de que reaccionan El estado ha cambiado.
Avíseme si el ejemplo es confuso o no es lo suficientemente claro como para proporcionar una mejor explicación.
En cuanto a: Esto significa que el estado consumido por su componente de destino puede tener una estructura muy diferente del estado tal como se almacena en su tienda.
¡No recibí la pregunta, pero solo sé que el estado de reacción (
this.setState
) es totalmente diferente del estado de Redux Store!
El estado de reacción se utiliza para manejar el redibujo y el comportamiento del componente de reacción. El estado de reacción está contenido exclusivamente en el componente.
El estado de Redux Store es una combinación de estados de reductores de Redux, cada uno es responsable de administrar una pequeña porción de la lógica de la aplicación.
Se puede acceder a esos atributos reductores con la ayuda de
react-redux::connect@mapStateToProps
por cualquier componente.
Lo que hace que el estado de la tienda Redux sea accesible en toda la aplicación, mientras que el estado del componente es exclusivo para sí mismo.
React-Redux
connect
se utiliza para actualizar la tienda para cada acción.
import { connect } from ''react-redux'';
const AppContainer = connect(
mapStateToProps,
mapDispatchToProps
)(App);
export default AppContainer;
Es muy simple y claramente explicado en este blog .
Puede clonar el proyecto github o copiar y pegar el código de ese blog para comprender la conexión de Redux.
import React from ''react'';
import {connect} from ''react-redux'';
import Userlist from ''./Userlist'';
class Userdetails extends React.Component{
render(){
return(
<div>
<p>Name : <span>{this.props.user.name}</span></p>
<p>ID : <span>{this.props.user.id}</span></p>
<p>Working : <span>{this.props.user.Working}</span></p>
<p>Age : <span>{this.props.user.age}</span></p>
</div>
);
}
}
function mapStateToProps(state){ *state is your redux-store object*
return {
user:state.activeUser
}
}
export default connect(mapStateToProps)(Userdetails);