Context
使用场景
- 为了解决props的繁琐传递问题,Context提供一种组件间共享值的方式,不必显示通过组件数逐层传递props;
- 很多不同层级的组件需要访问同样一些的数据,例如获取当前认证的用户、主题或首选语言,类似于全局缓存;
- Context有时候可由组件组合(component composition,即把组件当做props传递)代替。
使用步骤
-
创建context:
const ThemeContext = React.createContext({theme: 'light', text: '按钮'});
ThemedButton.contextType = ThemeContext; -
赋值context:
<ThemeContext.Provider value={{theme: 'primary', text: '提交'}}>
这里的value值可以传字符串,数字或者对象;
如要更新context,可以通过更新state再把state传入context的方式实现。 -
渲染context:
static contextType = ThemeContext;
// React 会往上找到最近的 theme Provider,然后使用它的值。
return <div className={'btn '+this.props.theme}>{this.props.value}</div>
或
<ThemeContext.Consumer>
{
context => (
<div className={'btn '+this.context.theme}>{this.context.text}</div>
)
}
</ThemeContext.Consumer> -
context嵌套:
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>// 一个组件可能会消费多个 context
function Content() {
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
Refs转发
使用场景
- 需要将 ref 自动地通过组件传递到其一子组件;
- 父组件通过React.createRef()生成传入,子组件通过React.forwardRef((props, ref) => ())接收渲染。
Fragments
- 一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点,
如return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
); - 循环时key可以加在React.Fragment节点标签上。
高阶组件(HOC)
-
高阶组件是参数为组件,返回值为新组件的函数。
-
使用方法:
class ShowUserPermit extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>showUserPermit... {this.props.VIP}</div>
)
}
}
class ShowUserVipInfo extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>showUserVipInfo... {this.props.VIP}</div>
)
}
}function wrap(WrappedComponent) {
return class reComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
VIP: 1000
}
}
render() {
return <WrappedComponent VIP={this.state.VIP}/>
}
}
}const id = document.getElementById('example');
const OtherShowUserVipInfo = wrap(ShowUserVipInfo);
const OtherShowUserPermit = wrap(ShowUserVipInfo);
const ele = <React.Fragment>
<OtherShowUserVipInfo />
<OtherShowUserPermit />
</React.Fragment>
ReactDOM.render(ele, id); -
HOC 不会修改传入的组件,也不会使用继承来复制其行为。相反,HOC 通过将组件包装在容器组件中来组成新组件。HOC 是纯函数,没有副作用;
-
不要在 render 方法中使用 HOC;
深入JSX
-
在 JSX 类型中使用点语法:
const MyComponents = {
DatePicker: function DatePicker(props) {
return <div>Imagine a {props.color} datepicker here.</div>;
}
}function BlueDatePicker() {
return <MyComponents.DatePicker color="blue" />;
} -
用户定义的组件必须以大写字母开头;
-
在运行时选择类型:
function Story(props) {
// 错误!JSX 类型不能是一个表达式。
return <components[props.storyType] story={props.story} />;
}
function Story(props) {
// 正确!JSX 类型可以是大写字母开头的变量。
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
} -
JavaScript 表达式可作为 Props,如<MyComponent foo={1 + 2 + 3 + 4} />;
-
if 语句以及 for 循环不是 JavaScript 表达式,所以不能在 JSX 中直接使用,但是可以在JSX之外用;
-
Props 默认值为 True
-
props展开:
const props = {firstName: 'Ben', lastName: 'Hector'};
<Hello {...props}/> 与 <Hello firstName={props.firstName} lastName={props.lastName} />等价 -
如果你想渲染 false、true、null、undefined 等值,你需要先将它们转换为字符串:
<div>My JavaScript variable is {String(myVariable)}.</div>
Portals
ReactDOM.createPortal(child, container)
- Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案;
- 第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个 DOM 元素。
Render Props
- 在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术;
- 具有 render prop 的组件接受一个返回 React 元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑:
<DataProvider render={data => (<h1>{data.target}</h1>)}/> - 本质上是把一个回调函数传入到组件里,通过回调函数的参数data再次渲染指定组件;
- 任何被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 “render prop”,不一定非要起 render 名称;
- 实例代码如下:
// 以下组件跟踪 Web 应用程序中的鼠标位置
class Man extends React.Component {
constructor(props) {
super(props);
}
render() {
const mouse = this.props.mouse;
return <p>人散步:当前的人的位置是 ({mouse.x}, {mouse.y})</p>
}
}
class Cat extends React.Component {
constructor(props) {
super(props);
}
render() {
const mouse = this.props.mouse;
return <p>猫追老鼠:当前的猫的位置是 ({mouse.x}, {mouse.y})</p>
}
}
class Mouse extends React.Component {
constructor (props) {
super(props);
this.state = {
x: 0,
y: 0
};
this.handleMouseMove = this.handleMouseMove.bind(this);
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{height: '10vh'}} onMouseMove={this.handleMouseMove}>
{ /<Cat mouse={this.state}/>/ }
{
/*
使用render
prop 动态决定要渲染的内容,
而不是给出一个 <Mouse> 渲染结果的静态表示
b
*/
}
{
this.props.render(this.state,this.props.type)
}
</div>
)
}
}
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.renderMouse = this.renderMouse.bind(this);
}
renderMouse(mouse,Type) {
return <Type mouse={mouse}/>
}
render() {
return (
<div>
<h1>移动鼠标!</h1>
<Mouse type={Cat} render={this.renderMouse}/>
<Mouse type={Man} render={this.renderMouse}/>
</div>
)
}
}
const id = document.getElementById('example');
const ele = <React.Fragment>
<MouseTracker />
</React.Fragment>;
ReactDOM.render(ele, id);
网友评论