内容简介:就像人们对更新移动应用程序和操作系统感到兴奋一样,开发人员也应该对更新框架感到兴奋。不同框架的新版本具有新特性和开箱即用的技巧。下面是将现有应用程序从 React 15 迁移到 React 16 时应该考虑的一些好特性。React 16 引入了错误边界的新概念。
就像人们对更新移动应用程序和操作系统感到兴奋一样,开发人员也应该对更新框架感到兴奋。不同框架的新版本具有新特性和开箱即用的技巧。
下面是将现有应用程序从 React 15 迁移到 React 16 时应该考虑的一些好特性。
错误处理
React 16 引入了错误边界的新概念。
现在在React 16中,大家就能使用错误边界功能,而不用一发生错误就解除整个程序挂载了。把错误边界看成是一种类似于编程中try-catch语句的机制,只不过是由 React 组件来实现的。
错误边界是一种React组件。它及其子组件形成一个树型结构,能捕获JavaScript中所有位置的错误,记录下错误,并且还能显示一个后备界面,避免让用户直接看到组件树的崩溃信息。
这里涉及到一种新的生命周期函数叫 componentDidCatch(error, info) 。无论什么样的类组件,只要定义了这个函数,就成为了一个错误边界。
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// 你还可以将错误记录到错误报告服务中
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// 可以渲染任何自定义回退界面
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
也可以将其用作常规组件使用:
<ErrorBoundary> <MyWidget /> </ErrorBoundary>
componentDidCatch() 方法的工作原理类似于JavaScript catch{} 块,但它适用于组件。只有类组件可以是错误边界。实际上,在大多数情况下,你都希望声明一次错误边界组件,然后在整个应用程序中使用它。
请注意,错误边界只会捕获位于它们之下的组件中的错误。错误边界无法捕获到自身的错误。如果错误边界渲染错误消息失败,错误将被传播到上方最接近的错误边界。这也类似于 JavaScript 中的 catch{}块。
有了错误边界,即使某个组件的结果有错误,整个React程序挂载也不会被解除。只有出错的那个组件会显示一个后备界面,而整个程序仍然完全正常运行。
关于错误边界更多的内容可查看 官网 。
新的 render 返回类型:片段和字符串
现在,在渲染时可以摆脱将组件包装在 div 中。
你现在可以从组件的 render 方法返回元素数组。与其他数组一样,你需要为每个元素添加一个键以避免发出键警告:
render() {
// No need to wrap list items in an extra element!
return [
// Don't forget the keys :)
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}
从React 16.2.0开始 ,它支持JSX的一个特殊片段语法,该语法不需要键。
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
支持返回字符串:
render() {
return 'Look ma, no spans!';
}
Portal
Portal 提供了一种将子节点渲染到父节点之外的 dom 节点。
ReactDOM.createPortal(child, container)
第一个参数 ( child )是任何可渲染的 React子元素,例如元素,字符串或片段。 第二个参数 (container) 是 DOM 元素。
如何使用它
在 React15.X 版本中,我们只能讲子节点在父节点中渲染,基本用法如下:
render() {
// React需要创建一个新的div来包含子节点
return (
<div>
{this.props.children}
</div>
);
}
但是如果需要将子节点插入到父节点之外的dom呢,React15.x 及之前都没有提供这个功能的 API。
可以使用 React16.0 中的 portal:
render() {
// React不需要创建一个新的div去包含子元素,直接将子元素渲染到另一个
//dom节点中
//这个dom节点可以是任何有效的dom节点,无论其所处于dom树中的哪个位置
return ReactDOM.createPortal(
this.props.children,
domNode,
);
}
Portal 的一个典型用例是这样的:当父组件带有 overflow:hidden 或 z-index 样式时,你希望子组件在视觉上能够“突破”它的容器。例如,对话框、悬停卡和 工具 提示。
自定义 DOM 属性
React15 会忽略任何未知的 DOM 属性。React 会跳过它们,因为无法识别它们。
// 你的代码 <div mycustomattribute="something" />
React 15 将渲染一个空的 div:
// React 15 output: <div />
在 React16 中,输出将如下所示(会显示自定义属性,并且完全不会被忽略)
// React 16 output: <div mycustomattribute="something" />
在 state 中设置 null 避免重新渲染
有时候我们需要通过函数来判断组件状态更新是否触发重新渲染,在 React 16 中,我们可以通过调用 setState 时传入 null 来避免组件重新渲染,这也就意味着,我们可以在 setState 方法内部决定我们的状态是否需要更新,
const MAX_PIZZAS = 20;
function addAnotherPizza(state, props) {
// Stop updates and re-renders if I've had enough pizzas.
if (state.pizza === MAX_PIZZAS) {
return null;
}
// If not, keep the pizzas coming! :D
return {
pizza: state.pizza + 1,
}
}
this.setState(addAnotherPizza);
更多相关信息请阅读 这里
创建 ref
现在使用 React16 创建 refs 要容易得多。 为什么需要使用 refs :
- 管理焦点、文本选择或媒体播放。
- 触发动画。
- 与第三方 DOM 库集成。
ref 是使用 React.createRef() 创建的,并通过 ref 属性附加到 React 元素。 ref 通常是在构造组件时被分配给实例的属性,以便在整个组件中引用它们。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render () {
return <div ref={this.myRef}></div>
}
}
访问 ref
上述是创建 Ref 指向的方法, 在Ref 所指向的组件,在 render 后就可以调用,React16.3 中提供了 current 属性,用于引用 render 后的节点:
componentDidMount(){
console.log(this.myRef.current);
//render之后就可以输出该ref指向的那个节点
}
此外,同样的 Ref 所指向的节点可以是 dom 节点,也可以是类组件。
Ref 的值因节点的类型不同而有所不同:
- 当 ref 属性用于 HTML 元素时,在构造函数中使用 React.createRef() 创建的 ref 将底层 DOM 元素作为 current 属性。
- 当 ref 属性用于自定义类组件时,ref 对象将已挂载的组件实例作为 current 属性。
- 你可能不会在功能组件上使用 ref 属性,因为它们没有实例。
Context API
Context 提供了一种通过组件树传递数据的方法,无需在每一层手动传递 prop 。
React.createContext
const { Provider, Consumer } = React.createContext(defaultValue)
创建 {Provider,Consumer} 对。当 React 渲染 Consumer 时,它将从树中最接近的 Provider 读取当前上下文值。
defaultValue 参数只在消费者在树中找不到匹配的 Provider 时才会用到,这在单独测试组件时十分有用。注意:将 undefined 作为 Provider 值传递进去并不会导致 Consumer 使用 defaultValue 。
Provider
<Provider value={/* some value */}>
一个允许 Consumer 订阅上下文变更的 React 组件。
一个 Provider 可以连接多个 Consumer,可以在树中嵌套 Provider,实现更深的值覆盖。
Consumer
<Consumer>
{value => /* render something based on the context value */}
</Consumer>
订阅上下文更改的 React 组件。
需要一个函数作为子组件。这个函数接收当前上下文值,并返回一个 React 节点。传给函数的 value 参数将等于树中最近的 Provider 的 value 。如果没有匹配的 Provider,则 value 参数将等于传给 createContext() 的 defaultValue。
static getDerivedStateFromProps()
在很长一段时间内, componentWillReceiveProps 是在没有附加渲染的情况下更新状态的唯一方法。
在版本16.3中,我们引入了一个全新的生命周期函数 getDerivedStateFromProps 用来替换 componentWillReceiveProps ,并用更安全的方式处理相同的场景。
与此同时,我们意识到人们对如何使用这两种方法有很多误解,我们发现了一些反模式,这些错误导致了微妙而令人困惑的bug。
在16.4中,有关 getDerivedStateFromProps 的修复使得派生状态更加可预测,因此错误使用的结果更容易被注意到。
getDerivedStateFromProps 会在调用 render 方法之前被调用,它应该返回一个用于更新状态的对象,或者如果不更新任何状态就返回 null 。
这个方法适用于一些罕见的用例,其中 state 依赖 prop 的变化。例如,可以很方便地实现一个 <Transition> 组件,它会比较上一个和下一个子组件,然后决定它们中的哪个需要进行动画渲染。
衍生 state 会导致冗长的代码,并让你的组件难以开发和维护。
你可以考虑更简单的替代方案:
- 如果你需要在 prop 发生变更时做一些其他事情(例如数据提取或动画),请改用
componentDidUpdate生命周期。 - 如果你只想在 prop 发生变更时重新计算某些数据,请改用 memoization helper :
*如果你想在 prop 发生变更时“重置”某个状态,请考虑创建受控组件或带有键的非受控组件。
- 此方法无权访问组件实例。 如果你愿意,可以通过提取组件
props的纯函数和类定义之外的状态,在getDerivedStateFromProps()和其他类方法之间重用一些代码。
注意,不管怎样,这个方法都会在每次进行渲染时触发。这与 UNSAFE_componentWillReceiveProps 完全相反。它只在父组件进行重新渲染时触发,而且不作为本地 setState 的结果。
将 nextProps.someValue 与this.props.someValue进行比较。 如果两者都不同,那么我们执行一些操作:
static getDerivedStateFromProps(nextProps, prevState){
if(nextProps.someValue!==prevState.someValue){
return { someState: nextProps.someValue};
}
return null
}
它接收两个参数 nextProps 和 prevState 。如前所述,你无法在这个方法中访问 this 。你必须将 prop 存储在 state 中,然后将 nextProps 与之前的 prop 进行对比。在上面的代码中,nextProps 和 prevState 进行了比较。如果两者不同,则返回一个用于更新状态的对象,否则就返回 null,表示不需要更新状态。如果 state 发生变更,就会调用 componentDidUpdate,我们可以像在 componentWillReceiveProps 中那样执行所需的操作。
React 生命周期事件
react v16.3,最大的变动莫过于生命周期去掉了以下三个:
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
同时为了弥补失去上面三个周期的不足又加了两个:
- static getDerivedStateFromProps
- getSnapshotBeforeUpdate
为什么要改
旧的生命周期十分完整,基本可以捕捉到组件更新的每一个state/props/ref,没有什从逻辑上的毛病。
但是架不住官方自己搞事情,react打算在17版本推出新的Async Rendering,提出一种可被打断的生命周期,而可以被打断的阶段正是实际dom挂载之前的虚拟dom构建阶段,也就是要被去掉的三个生命周期。
生命周期一旦被打断,下次恢复的时候又会再跑一次之前的生命周期,因此componentWillMount,componentWillReceiveProps, componentWillUpdate都不能保证只在挂载/拿到props/状态变化的时候刷新一次了,所以这三个方法被标记为不安全。
你的点赞是我持续分享好东西的动力,欢迎点赞!
欢迎加入前端大家庭,里面会经常分享一些技术资源。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 物联网对黑客来说是个好消息
- Mozilla CEO:微软“投诚” Chromium 对用户来说是灾难
- 全球超50000台服务器遭攻击,它说是中国黑客干的
- 仅仅知道如何终止XHR请求,或许对你来说是不够的!
- 我司用了 6 年的 Redis 分布式限流器,可以说是非常厉害了
- 我司用了 6 年的 Redis 分布式限流器,可以说是非常厉害了
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Don't Make Me Think
Steve Krug / New Riders Press / 18 August, 2005 / $35.00
Five years and more than 100,000 copies after it was first published, it's hard to imagine anyone working in Web design who hasn't read Steve Krug's "instant classic" on Web usability, but people are ......一起来看看 《Don't Make Me Think》 这本书的介绍吧!