我们先看一个非常常见的例子,一个输入框需要从本地获取数据将获取的数据放到输入框内
export default class Username extends React.Component {
constructor(){
super();
this.state = {val:''}
}
componentDidMount(){
let username = localStorage.getItem('username')||'';
this.setState({
val:username
})
}
render(){
return <div>
<input type="text" value={this.state.val} onChange={()=>{}}/>
</div>
}
}
这段逻辑可能在Password组件中也要使用,那么从本地存储中获取数据放到输入框内的逻辑应该就是公用逻辑。这时我们就要使用高阶组件,也就是将组件在原有的基础上进行包装。
import React from 'react';
let local = (key)=>(Component)=>{
return class HighOrderComponent extends React.Component{
constructor(){
super();
this.state = {val:''}
}
componentDidMount(){
let username = localStorage.getItem(key)||'';
this.setState({
val:username
})
}
render(){
return <Component {...this.state}/>
}
}
};
export default local;
import Local from './Local'
class Username extends React.Component {
render(){
return <div>
<input type="text" value={this.props.val} onChange={()=>{}}/>
</div>
}
}
export default Local('username')(Username);
我们将公共的逻辑拿到外层组件,处理好后以属性的方式传递给原本的组件,为此高阶组件就是一个 React 组件包裹着另外一个 React 组件
react是单向数据流,我们想传递数据需要一层层向下传递,数据传递变得非常麻烦,我们可以用context实现数据的交互
1) 父 childContextTypes getChildContext函数
2) 子 contextTypes
App |-> header -> title
import React from 'react';
import PropTypes from 'prop-types'
import Header from "./Header";
export default class App extends React.Component {
constructor(){
super();
this.state = {color:'red'}
}
static childContextTypes = { //定义子组件上下文的类型
color:PropTypes.string,
setColor:PropTypes.func
};
setColor = (color) =>{
this.setState({
color
})
};
getChildContext(){ // 定义子组件上下文的数据
return {color:this.state.color,setColor:this.setColor}
}
render(){
return <div>
<Header/>
</div>
}
}
export default class Header extends React.Component {
static contextTypes = {
setColor:PropTypes.func
};
render(){
return <div>
<button onClick={()=>{
this.context.setColor('green');
}}>变绿</button>
<Title/>
</div>
}
}
export default class Title extends React.Component {
static contextTypes = {
color:PropTypes.string
};
render(){ // 通过context获取父组件定义的数据
return <div style={{color:this.context.color}}>Title</div>
}
}
Todos |-> TodoHeader
|-> TodoItems
|-> TodoFooter
和以前写过的逻辑一致,这回加上react-redux的逻辑
import React from 'react';
import ReactDOM from 'react-dom';
import Counter from "./components/Counter";
import store from './store/index';
import {Provider} from 'react-redux';
ReactDOM.render(
<Provider store={store}>
<Counter/>
</Provider>,window.root);
// counter组件
class Counter extends React.Component {
render(){
return <div>
数量:{this.props.number}
<button onClick={()=>{this.props.add(1)}}>+</button>
<button onClick={()=>{this.props.minus(1)}}>-</button>
</div>
}
}
export default connect(state=>({...state}),dispatch=>({
add:(amount)=>{dispatch(actions.add(amount))},
minus:(amount)=>{dispatch(actions.minus(amount))}
}))(Counter)
import React from 'react';
import PropTypes from 'prop-types';
class Provider extends React.Component{
static childContextTypes = {
store:PropTypes.object
};
getChildContext(){
return {store:this.props.store}
}
constructor(){
super();
}
render(){
return this.props.children;
}
}
let connect = (mapStateToProps,mapDispatchToProps) => (Component) =>{
return class Proxy extends React.Component{
static contextTypes = {
store:PropTypes.object
};
componentDidMount(){
this.unsubscribe = this.context.store.subscribe(()=>{
this.setState(mapStateToProps(this.context.store.getState()))
});
}
componentWillUnmount(){
this.unsubscribe();
}
constructor(props,context){
super();
this.state = mapStateToProps(context.store.getState());
}
render(){
return <Component {...this.state} {...mapDispatchToProps(this.context.store.dispatch)}/>
}
}
};
export {Provider,connect}
let bindActionCreators = (actions,dispatch) => {
let obj = {}
for(let key in actions){
obj[key] = (...args)=>{
dispatch(actions[key](...args))
}
}
return obj
};
export default connect(state=>({...state}),dispatch=>bindActionCreators(actions,dispatch))(Counter)
bindActionCreators是redux中的一个方法,并且这样的逻辑过于复杂,我们依旧希望可以在react-redux中内部可以简化操作
export default connect(state=>({...state}),actions)(Counter);
import {bindActionCreators} from './redux'
render(){
let r ={}
if(typeof mapDispatchToProps === 'object'){
r = bindActionCreators(mapDispatchToProps,this.context.store.dispatch)
}else{
r = mapDispatchToProps(this.context.store.dispatch)
}
return <Component {...this.state} {...r}/>
}
这样我们在组件中更改状态时可以直接传入actionCreator对象。