我试图了解 react-redux 的 connect 方法,以及它作为参数的函数。特别是 mapStateToProps()
。
按照我的理解,mapStateToProps
的返回值将是一个从状态派生的对象(因为它存在于存储中),其键将作为道具传递给您的目标组件(应用组件连接)。
这意味着目标组件使用的状态可能与存储在存储中的状态具有截然不同的结构。
问:这样可以吗?问:这是预期的吗?问:这是反模式吗?
this.props.someData
而不是 this.props.someKey[someOtherKey].someData
...有意义吗?
是的,这是正确的。它只是一个辅助函数,可以更简单地访问您的状态属性
假设您的应用程序 state.posts
中有一个 posts
键
state.posts //
/*
{
currentPostId: "",
isFetching: false,
allPosts: {}
}
*/
和组件 Posts
默认情况下 connect()(Posts)
将使所有状态道具可用于连接的组件
const Posts = ({posts}) => (
<div>
{/* access posts.isFetching, access posts.allPosts */}
</div>
)
现在,当您将 state.posts
映射到您的组件时,它会变得更好一些
const Posts = ({isFetching, allPosts}) => (
<div>
{/* access isFetching, allPosts directly */}
</div>
)
connect(
state => state.posts
)(Posts)
mapDispatchToProps
通常你必须写 dispatch(anActionCreator())
使用bindActionCreators
,您也可以更轻松地做到这一点,例如
connect(
state => state.posts,
dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)
)(Posts)
现在您可以在组件中使用它
const Posts = ({isFetching, allPosts, fetchPosts, deletePost }) => (
<div>
<button onClick={() => fetchPosts()} />Fetch posts</button>
{/* access isFetching, allPosts directly */}
</div>
)
actionCreators 的更新..
actionCreator 示例:deletePost
const deletePostAction = (id) => ({
action: 'DELETE_POST',
payload: { id },
})
因此,bindActionCreators
只会执行您的操作,将它们包装到 dispatch
调用中。 (我没有阅读redux的源代码,但实现可能看起来像这样:
const bindActionCreators = (actions, dispatch) => {
return Object.keys(actions).reduce(actionsMap, actionNameInProps => {
actionsMap[actionNameInProps] = (...args) => dispatch(actions[actionNameInProps].call(null, ...args))
return actionsMap;
}, {})
}
问:Is this ok?
答:是的
问:Is this expected?
是的,这是意料之中的(如果您使用的是 react-redux)。
问:Is this an anti-pattern?
答:不,这不是反模式。
它被称为“连接”您的组件或“使其智能”。这是设计使然。
它允许您将组件与状态解耦,从而增加代码的模块化。它还允许您将组件状态简化为应用程序状态的子集,这实际上有助于您遵守 Redux 模式。
这样想:存储应该包含应用程序的整个状态。对于大型应用程序,这可能包含数十个嵌套很多层的属性。您不想在每次通话时都拖拉所有这些(昂贵)。
如果没有 mapStateToProps
或其类似物,您会很想以另一种方式划分您的状态以提高性能/简化。
(1) -
更轻松的深度访问 (2) -
避免组件可能会弄乱不属于它的状态的错误
你说对了第一部分:
是的,mapStateToProps
将 Store 状态作为参数/参数(由 react-redux::connect
提供),它用于将组件与 store 状态的某些部分链接起来。
通过链接,我的意思是 mapStateToProps
返回的对象将在构建时作为道具提供,任何后续更改都将通过 componentWillReceiveProps
获得。
如果您知道观察者设计模式,那正是它的那个或很小的变体。
一个例子将有助于使事情更清楚:
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);
可以有另一个名为 itemsFilters
的 react 组件处理显示并将过滤器状态持久化为 Redux Store 状态,Demo 组件正在“侦听”或“订阅” Redux Store 状态过滤器,因此每当过滤器存储状态更改时(在帮助下filtersComponent
的)react-redux 检测到有更改并通过将更改发送到它们的 componentWillReceiveProps
来通知或“发布”所有侦听/订阅的组件,在本例中这将触发项目的重新过滤并刷新显示由于反应状态发生了变化。
让我知道该示例是否令人困惑或不够清晰,无法提供更好的解释。
至于:这意味着目标组件使用的状态可能与存储在商店中的状态具有截然不同的结构。
我没有得到问题,但只知道反应状态(this.setState
)与 Redux Store 状态完全不同!
反应状态用于处理反应组件的重绘和行为。反应状态仅包含在组件中。
Redux Store 状态是 Redux reducer 状态的组合,每个状态负责管理一小部分应用程序逻辑。任何组件都可以在 react-redux::connect@mapStateToProps
的帮助下访问这些 reducers 属性!这使得 Redux 存储状态可在应用程序范围内访问,而组件状态是其自身独有的。
这个react & redux 示例基于 Mohamed Mellouki 的示例。但使用 prettify 和 linting rules 进行验证。请注意,我们使用 PropTypes 定义了我们的 props 和 dispatch 方法,这样我们的编译器就不会对我们大喊大叫。此示例还包括 Mohamed 示例中缺少的一些代码行。要使用 connect,您需要从 react-redux 导入它。此示例还 binds 方法 filterItems 这将防止 component 中的 scope 问题。此源代码已使用 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);
此示例代码是一个很好的模板,可以作为您的组件的起点。
React-Redux connect
用于为每个操作更新存储。
import { connect } from 'react-redux';
const AppContainer = connect(
mapStateToProps,
mapDispatchToProps
)(App);
export default AppContainer;
此blog中的解释非常简单明了。
您可以克隆 github 项目或从该博客复制粘贴代码以了解 Redux 连接。
这是一个简单的概念。 Redux 从 reducer 中的操作创建一个无处不在的状态对象(存储)。与 React 组件一样,此状态不必在任何地方显式编码,但它可以帮助开发人员在 reducer 文件中查看默认状态对象,以可视化正在发生的事情。您在组件中导入 reducer 以访问该文件。然后 mapStateToProps 仅选择其组件需要的存储中的键/值对。把它想象成 Redux 创建一个 React 组件的全局版本
this.state = ({
cats = [],
dogs = []
})
使用 mapStateToProps() 来改变状态的结构是不可能的。您正在做的是仅选择组件需要的商店的键/值对并将值(从商店中的键/值列表)传递到组件中的道具(本地键)。您在列表中一次执行一个值。在此过程中不会发生结构变化。
PS 商店是本地状态。 Reducers 通常也会将状态传递给数据库,Action Creators 也参与其中,但首先要了解这个简单的概念,以了解这个特定的帖子。
PPS 最好将 reducer 分成每个文件的单独文件,并且只导入组件需要的 reducer。
下面是描述 mapStateToProps
行为的大纲/样板:
(这是 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} />;
}
}
接下来
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} />;
}
}
}
是的,你可以这样做。您甚至可以处理状态并返回对象。
function mapStateToProps(state){
let completed = someFunction (state);
return {
completed : completed,
}
}
如果您想将与状态相关的逻辑从渲染函数转移到它之外,这将很有用。
我想重新构建您提到的声明,即:
这意味着目标组件使用的状态可能与存储在存储中的状态具有截然不同的结构
你可以说你的目标组件使用的状态有一小部分存储在 redux 存储中。换句话说,组件使用的状态将是 redux 存储状态的子集。
就理解 connect() 方法而言,它相当简单! connect() 方法有能力为你的组件添加新的道具,甚至覆盖现有的道具。正是通过这个 connect 方法,我们也可以访问由 Provider 抛出的 redux store 的状态。结合使用对您有利,您可以将 redux 存储的状态添加到组件的 props 中。
以上是一些理论,我建议您看一下这个 video 以更好地理解语法。
students:{...}
),该集合是具有唯一键的对象,每个键对应于具有共同属性的对象(即 {[uuid]:{first_name, last_name, age, major, minor}}
)。组件可以将其转换为这些属性之一的唯一值数组(即 [...major]
)。不同的组件可能会以不同的方式对其进行转换(即 [...${first_name} ${last_name}
])
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){
return {
user:state.activeUser
}
}
export default connect(mapStateToProps, null)(Userdetails);
不定期副业成功案例分享
dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)
从哪里获取fetchPosts
和deletePost
操作?state => state.posts
(mapStateToProps
函数)将告诉 React 更新时哪些状态将触发组件的重新渲染。