高阶组件与函数作为子组件
高阶组件的概念
高阶组件(HOC)是react中的一种设计模式,主要为了解决组件复用的问题,高阶组件接收一个组件作为参数,并且返回一个组件,
例如:
import React from "react";
export default function withTimer(WrappedComponent) {
return class extends React.Component {
state = { time: new Date() };
componentDidMount() {
this.timerID = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
time: new Date()
});
}
render() {
return <WrappedComponent time={this.state.time} {...this.props} />;
}
};
}
这里将WrappedComponent组件作为一个参数并返回一个新的组件,这样将props交给了作为参数的组件,也就是属性代理
函数作为子组件
函数作为子组件同样是为了解决组件复用问题,主要思想在于将函数作为一个子组件,从而可以根据函数参数的变化动态渲染,例如下方示例代码中的
animal和color
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
export default class AdvancedTabSelector extends PureComponent {
static propTypes = {
value: PropTypes.object,
options: PropTypes.array,
onChange: PropTypes.func,
children: PropTypes.func
};
static defaultProps = {
value: null,
options: [],
onChange: () => {},
children: () => {}
};
render() {
const { options, value, onChange } = this.props;
return (
<div className="tab-selector">
<ul>
{options.map(opt => (
<li
key={opt.value}
className={`tab-item ${
opt.value === this.props.value ? "selected" : ""
}`}
onClick={() => this.props.onChange(opt.value)}
>
{opt.name}
</li>
))}
</ul>
<br />
<br />
{this.props.value && this.props.children(this.props.value)}
</div>
);
}
}
const colors = [
{ name: "Red", value: "red" },
{ name: "Blue", value: "blue" },
{ name: "Orange", value: "orange" }
];
const animals = [
{ name: "Tiger", value: "tiger" },
{ name: "Elephant", value: "elephant" },
{ name: "Cow", value: "cow" }
];
export class AdvancedTabSelectorSample extends PureComponent {
state = {
color: null
};
render() {
return (
<div>
<h3>Select color: </h3>
<AdvancedTabSelector
options={colors}
value={this.state.color}
onChange={c => this.setState({ color: c })}
>
{color => (
<span
style={{
display: "inline-block",
backgroundColor: color,
width: "40px",
height: "40px"
}}
/>
)}
</AdvancedTabSelector>
<br />
<br />
<br />
<h3>Select animal: </h3>
<AdvancedTabSelector
options={animals}
value={this.state.animal}
onChange={c => this.setState({ animal: c })}
>
{animal => (
<img width="100px" src={require(`../../images/${animal}.png`)} />
)}
</AdvancedTabSelector>
</div>
);
}
}
react-redux中的connect
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])连接 React 组件与 Redux store。
连接操作不会改变原来的组件类。 反而返回一个新的已与 Redux store 连接的组件类。
参数
[
mapStateToProps(state, [ownProps]): stateProps] (Function): 如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。如果你省略了这个参数,你的组件将不会监听 Redux store。如果指定了该回调函数中的第二个参数ownProps,则该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapStateToProps也会被调用(例如,当 props 接收到来自父组件一个小小的改动,那么你所使用的 ownProps 参数,mapStateToProps 都会被重新计算)。注意:在高级章节中,你需要更好地去控制渲染的性能,所用到的
mapStateToProps()会返回一个函数。在这种情况下,那个函数将被作为mapStateToProps()在独有的组件实例中调用。这样就允许你在每一个实例中去记录。你可以参考 #279 去测试和了解其中的详细内容。但在绝大多数的应用中不会用到。[
mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function): 如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中dispatch方法会将action creator的返回值作为参数执行。这些属性会被合并到组件的 props 中。如果传递的是一个函数,该函数将接收一个
dispatch函数,然后由你来决定如何返回一个对象,这个对象通过dispatch函数与 action creator 以某种方式绑定在一起(提示:你也许会用到 Redux 的辅助函数bindActionCreators())。如果你省略这个mapDispatchToProps参数,默认情况下,dispatch会注入到你的组件 props 中。如果指定了该回调函数中第二个参数ownProps,该参数的值为传递到组件的 props,而且只要组件接收到新 props,mapDispatchToProps也会被调用。注意:在高级章节中,你需要更好地去控制渲染的性能,所用到的
mapStateToProps()会返回一个函数。但在这个例子中,这个函数将被mapStateToProps()在独有的组件实例中调用。这样就允许你在每一个实例中去记录。你可以参考 #279 去测试和了解其中的详细内容。但在绝大多数的应用中不会用到。
- [
mergeProps(stateProps, dispatchProps, ownProps): props] (Function): 如果指定了这个参数,mapStateToProps()与mapDispatchToProps()的执行结果和组件自身的props将传入到这个回调函数中。该回调函数返回的对象将作为 props 传递到被包装的组件中。你也许可以用这个回调函数,根据组件的 props 来筛选部分的 state 数据,或者把 props 中的某个特定变量与 action creator 绑定在一起。如果你省略这个参数,默认情况下返回Object.assign({}, ownProps, stateProps, dispatchProps)的结果。- [
options] (Object) 如果指定这个参数,可以定制 connector 的行为。
- [
pure = true] (Boolean): 如果为 true,connector 将执行shouldComponentUpdate并且浅对比mergeProps的结果,避免不必要的更新,前提是当前组件是一个“纯”组件,它不依赖于任何的输入或 state 而只依赖于 props 和 Redux store 的 state。默认值为true。- [
withRef = false] (Boolean): 如果为 true,connector 会保存一个对被包装组件实例的引用,该引用通过getWrappedInstance()方法获得。默认值为false。
connect函数作用是连接react组件与store
import React from "react";
import { bindActionCreators, createStore } from "redux";
import { Provider, connect } from "react-redux";
// Store initial state
const initialState = { count: 0 };
// reducer
const counter = (state = initialState, action) => {
switch (action.type) {
case "PLUS_ONE":
return { count: state.count + 1 };
case "MINUS_ONE":
return { count: state.count - 1 };
case "CUSTOM_COUNT":
return { count: state.count + action.payload.count };
default:
break;
}
return state;
};
// Create store
const store = createStore(counter);
// Action creator
function plusOne() {
// action
return { type: "PLUS_ONE" };
}
function minusOne() {
return { type: "MINUS_ONE" };
}
export class Counter extends React.Component {
render() {
const { count, plusOne, minusOne } = this.props;
return (
<div className="counter">
<button onClick={minusOne}>-</button>
<span style={{ display: "inline-block", margin: "0 10px" }}>
{count}
</span>
<button onClick={plusOne}>+</button>
</div>
);
}
}
function mapStateToProps(state) {
return {
count: state.count
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ plusOne, minusOne }, dispatch);
}
const ConnectedCounter = connect(mapStateToProps, mapDispatchToProps)(Counter);
export default class CounterSample extends React.Component {
render() {
return (
<Provider store={store}>
<ConnectedCounter />
</Provider>
);
}
}