Je veux créer des modules réutilisables qui pourraient être connectés à n’importe quelle application react-redux. Idéalement, mon module aurait un composant conteneur, des actions et un réducteur au niveau supérieur (puis tous les composants de présentation situés sous le conteneur). Je voudrais que le module ne fonctionne que sur sa propre tranche de l'état de l'application, et idéalement pour ne rien connaître du reste de l'état de l'application (il est donc vraiment modulaire).
Les réducteurs ne fonctionnent que sur une partie de l'état (à l'aide de combineRéducteurs), alors je suis heureux là-bas. Cependant, avec les composants de conteneur, il semble que mapStateToProps prenne toujours tout l'état de l'application.
J'aimerais que mapStateToProps prenne uniquement la même "tranche d'état" que celle que je gère dans mon module (comme le fait le réducteur). De cette façon, mon module serait vraiment modulaire. Est-ce possible? Je suppose que je pourrais simplement transmettre cette partie de l'état aux accessoires de ce composant (pour pouvoir utiliser le second argument de mapStateToProps, ownProps), mais je ne suis pas sûr que cela aurait le même effet.
C'est en fait un sujet compliqué. Redux étant un magasin global unique, l'idée d'un ensemble de logique plug-and-play entièrement encapsulé et entièrement réutilisable devient plutôt difficile. En particulier, bien que la logique de réduction puisse être assez générique et ne pas savoir où elle se trouve, les fonctions de sélecteur doivent savoir où se trouvent les données dans l’arborescence.
La réponse spécifique à votre question est "non, mapState
reçoit toujours l'arborescence d'état complète".
J'ai des liens vers un certain nombre de ressources pertinentes qui pourraient éventuellement vous aider dans votre situation:
Comme vous le savez, Redux n’a qu’un seul magasin. Tout ce qu’il sait faire, c’est donc de transférer tout le magasin à votre fonction mapStateToProps. Cependant, à l'aide de la déstructuration d'objet, vous pouvez spécifier les propriétés souhaitées dans le magasin et ignorer le reste. Quelque chose comme "function mapStateToProps ({prop1, prop2})" capturerait uniquement ces deux propriétés dans le magasin et ignorerait le reste. Votre fonction reçoit toujours l'intégralité du magasin, mais vous indiquez que seuls ces accessoires vous intéressent.
Dans mon exemple, "prop1" et "prop2" seraient les noms que vous avez assignés à vos réducteurs lors de l'appel à "combineRéducteurs".
Je rencontre le même problème car, comme vous l'avez dit, l'implémentation actuelle de redux/react-redux permet de scinder les réducteurs sur l'état juste, mais mapDispatchToProps always passe tout l'arbre de l'état.
https://stackoverflow.com/a/39757853/444794 n'est pas ce que je veux, car cela signifie que nous devons dupliquer toute notre logique de sélecteur dans chaque application react-redux qui utilise notre module.
Ma solution actuelle consiste à passer la tranche de l'état en tant qu'accessoire. Cela suit une sorte de modèle de composition mais en même temps supprime la propreté de l'accès direct à l'état, ce qui me déçoit.
Exemple:
Généralement, vous voulez faire ceci:
const mapStateToProps = (state) => {
return {
items: mySelector(state)
}
}
const mapDispatchToProps = (dispatch) => {
return {
doStuff: (item) => {
dispatch(doStuff(item))
}
}
}
class ModularComponent extends React.Component {
render() {
return (
<div>
{ this.props.items.map((item) => {
<h1 onclick={ () => this.props.doStuff(item) }>{item.title}</h1>
})}
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ModularComponent)
mais comme ce module est inclus dans une application où l'état est maintenant composé de plusieurs éléments (c'est-à-dire des valeurs-clés) plutôt que d'une liste d'éléments, cela ne fonctionnera pas. Ma solution de contournement ressemble plutôt à ceci:
const mapStateToProps = (_, ownProps) => {
return {
items: mySelector(ownProps.items)
}
}
const mapDispatchToProps = (dispatch) => {
return {
doStuff: (item) => {
dispatch(doStuff(item))
}
}
}
class ModularComponent extends React.Component {
render() {
return (
<div>
{ this.props.items.map((item) => {
<h1 onclick={ () => this.props.doStuff(item) }>{item.title}</h1>
})}
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ModularComponent)
Et l'application utilisant le module ressemble à:
const mapStateToProps = (state) => {
return {
items: state.items
stuffForAnotherModule: state.otherStuff
}
}
class Application extends React.Component {
render() {
return (
<div>
<ModularComponent items={ this.props.items } />
<OtherComponent stuff={ this.props.stuffForAnotherModule } />
</div>
)
}
}
export default connect(mapStateToProps)(Application)
Idéalement, la façon dont cela fonctionne est que vous obtenez l'état et que vous en extrayez les valeurs en utilisant des déconstructeurs. redux fonctionne sur le concept d'état unique
Par exemple:-
function mapStateToProps(state){
const { auth } = state //just taking a auth as example.
return{
auth
}
}
Bien que mapStateToProps
(la première fonction que vous transmettez à la connexion) soit transmise à l'ensemble du magasin, comme vous l'avez dit, son travail consiste à mapper des parties spécifiques de l'état sur le composant. Ainsi, seuls les éléments renvoyés par mapStateToProps seront mappés en tant qu'accessoires de votre composant.
Disons donc que votre état ressemble à ceci:
{
account: {
username: "Jane Doe",
email: "[email protected]",
password: "12345",
....
},
someOtherStuff: {
foo: 'bar',
foo2: 'bar2'
},
yetMoreStuff: {
usuless: true,
notNeeded: true
}
}
et votre composant a besoin de tout, de account
et foo
à someOtherStuff
, votre mapStateToProps
ressemblera à ceci:
const mapStateToProps = ({ account, someOtherStuff }) => ({
account,
foo: { someOtherStuff }
});
export default connect(mapStateToProps)(ComponentName)
alors votre composant aura les propriétés prop account
et foo
mappées à partir de votre état redux.