React-Redux-3 的用法
UI组件和容器组件
UI组件
class One extends Component { render() { let { n,dec,inc } = this.props return ( <div> <button onClick={dec}>-</button> {n} <button onClick={inc}>+</button> </div> ) } }复制代码
只负责展示UI,不带业务逻辑
没有状态(即
不使用this.state
这个变量)所有数据都由参数(
this.props
)提供不使用
任何 Redux 的API
特点
例子
容器组件
class OneContainer extends Component { constructor(props) { super(props) this.state = { n: store.getState().n } //参数是一个函数 store.subscribe(this.change) } //当store里数据修改的时候会执行这个回调函数 change = () => { this.setState({ n: store.getState().n }) } dec() { store.dispatch(actionCreator.decAction(1)) } inc() { store.dispatch(actionCreator.incAction(2)) } render(){ return <One n={this.state.n} inc={this.inc} dec={this.dec} /> } }复制代码
以上例子来源
负责管理数据和业务逻辑,不负责 UI 的呈现
带有内部状态
使用 Redux 的 API
特点
例子
小结:
UI组件一般都可以写成无状态组件。无状态组件性能比较高,它就是一个函数。而普通子组件,会有一些生命周期函数。当一个组件只负责页面的渲染,没有任何逻辑的时候就可以将这个组件写成一个性能较高的无状态组件。
UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑
将二者分离的原因:
connect()
作用
从 UI 组件生成容器组件,即将两类组件 (UI组件,容器组件) 连接起来
问题思考:
输入逻辑:外部的数据(即
state
对象)如何转换为 UI 组件的参数;输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去。
connect方法的完整API
import { connect } from 'react-redux' const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList)复制代码
两个参数定义UI组件的业务逻辑,(详见后文)
作用:用来建立 UI 组件的参数到
store.dispatch
方法的映射。后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。即定义了哪些用户的操作应该当作 Action,传给 Store。如果
mapDispatchToProps
是一个函数,会得到dispatch
和ownProps
(容器组件的props
对象)两个参数。作用:建立一个从(外部的)
state
对象到(UI 组件的)props
对象的映射关系。负责输入逻辑,即将state
映射到 UI 组件的参数(props
)何时引发UI渲染?
// 容器组件的代码 // <FilterLink filter="SHOW_ALL"> // All // </FilterLink> const mapStateToProps = (state, ownProps) => { return { active: ownProps.filter === state.visibilityFilter } }复制代码
connect
方法可以省略mapStateToProps
参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。mapStateToProps
会订阅 Store,每当state
更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。mapStateToProps
的第一个参数总是state
对象,还可以使用第二个参数,代表容器组件的props
对象。使用
ownProps
作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染(暂时没有看懂例子)。mapStateToProps
mapDispatchToProps
const mapDispatchToProps = ( dispatch, ownProps ) => { return { onClick: () => { dispatch({ type: 'SET_VISIBILITY_FILTER', filter: ownProps.filter }); } }; }复制代码
从上面代码可以看到,
mapDispatchToProps
作为函数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。
connect 将当前组件和 state,action 联系起来,使得可以在组件中调用action中的方法和state中的数据 该段来源
@connect Decorator - 装饰器(文章来源)
//定义一个函数,也就是定义一个Decorator,target参数就是传进来的Class。 //这里是为类添加了一个静态属性 function testable(target) { target.isTestable = true; } //在Decorator后面跟着Class,Decorator是函数的话,怎么不是testable(MyTestableClass)这样写呢? //我只能这样理解:因为语法就这样,只要Decorator后面是Class,默认就已经把Class当成参数隐形传进Decorator了。 @testable class MyTestableClass {} console.log(MyTestableClass.isTestable) // true复制代码
import { connect } from 'react-redux'; class MyApp extends React.Component { // ...define your main app here } export default connect(mapStateToProps, mapDispatchToProps)(MyApp);复制代码
import { connect } from 'react-redux'; @connect(mapStateToProps, mapDispatchToProps) export default class MyApp extends React.Component { // ...define your main app here }复制代码
第一步需要安装npm依赖
第二步需要在package.json文件中配置一下
使用装饰器需要的步骤
npm install --save babel-plugin-transform-decorators-legacy复制代码
"plugins": [\ ["@babel/plugin-proposal-decorators", { "legacy": true }],\ ]复制代码
不用装饰器和使用装饰器的区别
装饰器(Decorator)是一个函数,用来修改类的行为。
使用装饰器
不用装饰器
< Provider > 组件
connect
方法存在的不足:使用该方法生成容器组件以后,需要让容器组件拿到state
对象,才能生成 UI 组件的参数。import { Provider } from 'react-redux' import { createStore } from 'redux' import todoApp from './reducers' import App from './components/App' let store = createStore(todoApp); render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )复制代码
上面代码中,
Provider
在根组件外面包了一层,这样一来,App
的所有子组件就默认都可以拿到state
了。低效的解决方案:将
state
对象作为参数,传入容器组件,一级一级将state传下去以备组件使用。高效的解决方案:React-Redux 提供
Provider
组件,可以让容器组件拿到state
。
React- Router 路由库
使用React-Router
的项目,与其他项目没有不同之处,也是使用Provider
在Router
外面包一层,毕竟Provider
的唯一功能就是传入store
对象。
const Root = ({ store }) => ( <Provider store={store}> <Router> <Route path="/" component={App} /> </Router> </Provider> );
作者:Simon、
链接:https://juejin.cn/post/7032487201573175304