学习 React官方文档整理如下
JSX
JSX 防止注入攻击,可以安全地在 JSX 当中插入用户输入内容
React DOM 在渲染所有输入内容之前,默认会进行转义成了字符串。可以确保在应用中不会注入那些并非自己明确编写的内容。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。
vue 亦然{{转义成字符串}}
本质是React.createElement
const title = response.potentiallyMaliciousInput;
// 直接使用是安全的:
const element = (
<h1 className="greeting">
{title}
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
(title)转译
);
- 在编译之后,JSX 表达式会被转为普通 JavaScript 函数调用,并且对其取值后得到 JavaScript 对象。
- 可以在 if 语句和 for 循环的代码块中使用 JSX,将 JSX 赋值给变量,把 JSX 当作参数传入,以及从函数中返回 JSX,然后通过{JSX}可以将JSX注入DOM中。
- 通过花括号包裹代码,可以在 JSX 中嵌入任何表达式。
- 利用JavaScript 中的逻辑与 (&&) 运算符,三目运算符,可以很方便地进行元素的条件渲染。
true && expression 返回 expression
false && expression 总是会返回 false
隐藏组件:
- 在组件的 render 方法中返回 null,不进行任何渲染。 并且不会影响组件的生命周期,钩子函数依然会被调用
- CSS样式,适用于频繁切换display: !isEditing ? undefined : 'none'
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}
父组件与子组件
- 标签传入的属性自动挂载在子组件的props对象下。
- 在父组件中使用prop.children 来将他们的子组件传递到渲染结果
prop.children
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
?ReactDOM.createPortal
弹窗、全局组件等开发的情况,这时候我们可能根据不同的需求需要把组件渲染至指定页面
ReactDOM.createPortal(child, container)
组件
- style对象是常量,style.margin可变
- 组件名称必须以大写字母开头
- React 元素默认是不可变对象,一旦被创建,就无法更改其子元素、属性。更新 UI 唯一的方式是创建一个全新的元素,并将其传入 ReactDOM.render()
- props用于定义外部接口,state用于记录内部状态
- props改变了,React会重渲染该节点及其子节点。props主要用于整个组件树中传递数据和配置。
- 在 JavaScript 中对象和数组是通过引用传入的,编程实践中我们会遵循子组件不修改props,保证单一数据流,子组件不影响到父组件
- 组件可以选择把它的 state 作为 props 向下传递到它的子组件中,父组件可以通过children prop 来将他们的子组件传递到渲染结果中
- 大多数情况下都不要使用prop来为State的初始化赋值,这会让数据来源不唯一,这常常会导致Bug。数据源唯一是最佳实践
- DOM渲染更新时候框架底层会做优化:浅比较
对于组件的state选取,可以尝试通过以下步骤来判断:
- 对于应用中的每一个 state:
- 找到根据这个 state 进行渲染的所有组件。
- 找到他们的共同所有者(common owner)组件(在组件层级上高于所有需要该 state 的组件)。
- 该共同所有者组件或者比它层级更高的组件应该拥有该 state。
- 如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置。
在React的组件中可以在两个位置来初始化state:
- 在组件的constructor中,等同于在类内部;
- 直接在class中利用属性赋值的方式this.setState
组件更新
根据props和state的是否变化来判断是否重新渲染组件
补充_对比react、vue
- react是单向数据绑定,vue中的特色是双向数据绑定。
- 其实两者都提倡单向数据流去管理状态。vuex和redux状态管理器就是单向,只是vue为UI控件提供了双向数据绑定,适应一些需要实时反应用户输入的场合,但通常在复杂应用中这种便利比不上引入状态管理带来的优势。
v-model本质就是value 的单向绑定 + onChange 事件侦听
在大型项目中React 优势不言而喻
- Vue 的数据对象、 React 的状态对象
- Vue 的单文件组件,使用 < template> 、< script> 分割代码,直接导致的问题就是上下文丢失,方法需要通过methods传递
- Vue 推荐的方案只有强耦合的 Vuex(Redux 迁移到 Vue 等不算在内)
- React 周边方案有 Redux、Mobx 等。这些库不会与 React 有太强的耦合(可以独立存在)。
定义组件
定义组件的方式:
- 函数组件:编写 JavaScript 函数,返回JSX
函数式组件就只有props没有state,hooks方式引入state
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
- class组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
定时器例子
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
关于 setState()
- 将传参对象合并到组件当前的状态值,构建一个新的虚拟DOM,对比之后更新
- this.props 和 this.state 可能会异步更新,出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用,所以不要依赖他们的值来更新下一个状态。
传参对象,传参函数可以解决这个问题。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
组件设计
划分组件结构
前:
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
提取为
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
状态提升_组件共享数据
- 通过state提升,依靠自上而下的数据流,而不是尝试在不同组件间同步 state。 在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可通过props实现共享 父组件的state,多个组件通过调用父组件上 包装了setState的方法 对state进行修改,共享数据。
例子_根据传入props渲染不同的结果
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
class Calculator extends React.Component {
render() {
return (
<div>
<TemperatureInput scale="c" />
<TemperatureInput scale="f" />
</div>
);
}
}
例子_在父组件中同步子组件的数据
子组件对于父组件传入的 prop没有控制权 在 React 中,通过使用“受控组件”来解决这个问题。类似与 DOM 中的 接受 value 和 onChange 一样,子组件接收两个来自父组件 的props,一个值一个方法的调用
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}
handleCelsiusChange(temperature) {
this.setState({scale: 'c', temperature});
}
handleFahrenheitChange(temperature) {
this.setState({scale: 'f', temperature});
}
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} />
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onTemperatureChange(e.target.value);
}
render() {
const temperature = this.props.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
生命周期
组件创建的钩子函数
当组件实例被创建并插入 DOM 中的生命周期调用顺序如下:
- constructor() 初始化状态、事件处理程序
- static getDerivedStateFromProps()
- render()
- componentDidMount()
constructor(props) {
super(props);
// 不要在这里调用 this.setState()
//避免将 props 的值复制给 state!这是一个常见的错误
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
//会在组件挂载后(插入 DOM 树中)立即调用
//依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,适合实例化请求
//适合添加订阅的地方。如果添加了订阅,请不要忘记在 componentWillUnmount() 里取消订阅
}
组件更新的钩子函数
组件的 props 或 state 发生变化时,会触发更新的生命周期调用顺序如下:
- static getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
componentDidUpdate(prevProps, prevState, snapshot){
//会在更新后会被立即调用。首次渲染不会执行此方法。
// 典型用法(不要忘记比较 props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
组件卸载的钩子函数
当组件从 DOM 中移除时会调用:
componentWillUnmount(){
//会在组件卸载及销毁之前直接调用
//在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。
}
componentWillUnmount() 中不应调用 setState() 因为该组件将永远不会重新渲染
组件抛出错误的钩子函数
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
static getDerivedStateFromError()
componentDidCatch(error, info){
//error —— 抛出的错误。
//info —— 带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息。
//用于记录错误之类的情况:
}
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染可以显示降级 UI
return { hasError: true };
}
componentDidCatch(error, info) {
// "组件堆栈" 例子:
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
logComponentStackToMyService(info.componentStack);
}
render() {
if (this.state.hasError) {
// 你可以渲染任何自定义的降级 UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
setState() 和 forceUpdate()
- setState() 是异步操作,会批量推迟更新。这使得在调用 setState() 后立即读取 this.state 成为了隐患。
- 所以请使用 componentDidUpdate 或者 setState 的回调函数(setState(updater, callback)),保证在应用更新后触发。
- 如需基于之前的 state 来设置当前的 state,关注参数 updater 的内容。
- 默认当组件的 state 或 props 发生变化时,组件将重新渲染。
- 但是如果 render() 方法依赖于其他数据,则可以调用 forceUpdate() 强制让组件调用 render() 重新渲染。
- 应该避免使用 forceUpdate(),尽量在 render() 中使用 this.props 和 this.state。
例子
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
事件处理
- 为了解决跨浏览器的兼容性问题,React 根据 W3C 规范来将浏览器原生事件封装为合成事件,并传入设置的事件处理程序中。合成事件提供了与原生事件同样的接口。
- React 一般不需要使用 addEventListener 为已创建的 DOM 元素添加监听器,而是在该元素初始渲染的时候添加监听器即可。
- React实际上没有将事件附加到子节点本身,而是通过事件委托模式,使用单个事件监听顶层所有事件来优化性能。
function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
- 使用 ES6 class 语法定义一个组件的时候,通常的做法是将事件处理函数声明为 class 中的方法。
例如: Toggle 组件会渲染一个让用户切换开关状态的按钮:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
- 必须谨慎对待 JSX 回调函数中的 this,这并不是 React 特有的行为;这其实与 JavaScript 函数工作原理有关。
- 如果没有在方法后面添加 (),例如 onClick={this.handleClick},那么应该手动为这个方法绑定 this
- 在 JavaScript 中,class 的方法默认不会绑定(类的实例 this)。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
handleClick = () => {//绑定loggingButton
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
- 向处理程序传递参数: 箭头函数(显式)和 Function.prototype.bind (隐式)
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
列表
可以通过使用 {} 在 JSX 内构建一个DOM元素集合
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
实现一个基础的列表组件
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
- 提供key,数组元素中使用的 key 在其兄弟节点之间应该是独一无二的,不需要是全局唯一。
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
//提供key给列表
//<li>{number}</li>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
- 元素的 key 只有放在就近的数组上下文中才有意义,(在数组的层次上给每个item赋值)先简单理解为:在 map() 方法中的元素需要设置 key 属性 e.g:你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 ListItem元素上,而不是放在 ListItem 组件中的li元素上。
function ListItem(props) {
const value = props.value;
return (
// 错误!你不需要在这里指定 key:
<li key={value.toString()}>
{value}
</li>
);
//改为 return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 错误!元素的 key 应该在这里指定:
<ListItem value={number} />
// key 应该在数组的上下文中被指定
//改为 <ListItem key={number.toString()} value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
- key 会传递信息给 React ,但不会传递给你的组件。如果你的组件中需要使用 key 属性的值,请用其他属性名显式传递这个值
表单
- 表单元素通常会保持一些内部的 state
- 表单具有默认的 HTML 表单行为,即在用户提交表单后浏览到新页面
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '请撰写一篇关于你喜欢的 DOM 元素的文章.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('提交的文章: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
文章:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
select 受控组件
受控组件:state随着用户交互改变
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('你喜欢的风味是: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
选择你喜欢的风味:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">葡萄柚</option>
<option value="lime">酸橙</option>
<option value="coconut">椰子</option>
<option value="mango">芒果</option>
</select>
</label>
<input type="submit" value="提交" />
</form>
);
}
}
开启多选之后,传入value一个数组实现多选
<select multiple={true} value={['B', 'C']}>
input 受控组件
- 当需要处理多个 input 元素时,可以给每个元素添加 name 属性,在同一个处理函数中根据 event.target.name 的值选择要执行的操作。
- 在受控组件上通过 prop 指定 value 的值会阻止用户更改输入。但value 设置为 undefined 或 null,输入仍可编辑。
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
参与:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
来宾人数:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
input 非受控组件
使用 ref 来从 DOM 节点中获取表单数据:
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input
defaultValue="Bob"
type="text"
ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
input file 非受控
<input type="file" />始终是一个非受控组件,因为它的值只能由用户设置,而不能通过代码控制
允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用 JavaScript 的 File API 进行控制。
const selectedFile = document.getElementById('input').files[0];
class FileInput extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.fileInput = React.createRef();
}
handleSubmit(event) {
event.preventDefault();
alert(
`Selected file - ${this.fileInput.current.files[0].name}`
);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Upload file:
<input type="file" ref={this.fileInput} />
</label>
<br />
<button type="submit">Submit</button>
</form>
);
}
}
ReactDOM.render(
<FileInput />,
document.getElementById('root')
);
Context跨层级的组件通信
Context 设计目的是为了共享一个组件树的全局数据,例如当前认证的用户、主题或首选语言。即多个层级的多个组件需要访问相同数据
//创建一个 Context 对象
const MyContext = React.createContext(defaultValue);
//当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值,没有匹配到则采用defaultValue
<MyContext.Provider value={/* 某个值 */}>
//Consumer中取的就是Provider的value
- 每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。
- Provider组件利用Object.is检测 value 的值是否有更新,变化则其所有后代Consumers都会重新渲染。
- Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。
- Provider 包裹的组价内部可以通过Consumers访问到 Provider 的value值
Class.contextType
- 挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象
- 使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中。
class MyClass extends React.Component {
componentDidMount() {
let value = this.context;
/* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* 基于 MyContext 组件的值进行渲染 */
}
}
MyClass.contextType = MyContext;
错误边界
错误边界仅可以捕获其子组件的错误,它无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会冒泡至最近的上层错误边界,这也类似于 JavaScript 中 错误机制。 当抛出错误后
- 使用
static getDerivedStateFromError()` 渲染备用 UI - 使用
componentDidCatch()打印错误信息
Refs转发
- React数据流中,props是唯一的父组件与它们的子元素的通信方式。
- 更改子元素,你需要使用新的props去重新渲染子元素。
- React提供了一种特殊方法:Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧(慎用)
- 当ref属性用于HTML元素,在构造器中通过React.createRef()函数创建的ref接收底层DOM元素作为它的current属性;
- 当ref属性用于传统的类组件,ref对象接收挂载好的组件实例作为它的current
- 你不能将ref属性用于函数式组件上,因为他们并没有实例(instance),hooks方式可以实现
- 当属性值是一个回调函数的时候,函数将接受底层的DOM元素或组件的已挂载实例作为其第一个参数,组件更新时候触发回调
fragment
React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。
高阶组件HOC
- 组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。 e.g: Redux 的 connect 和 Relay 的 createFragmentContainer。 HOC 通过将组件包装在容器组件中来组成新组件。HOC 是纯函数,没有副作用。
React更新视图的方式:
- hydrate(ReactDOMServer渲染)
- ReactDOM.render() || ReactDOM.render(element, container,callback?){在c内渲染一个e,并返回对该组件的引用,该回调将在组件被渲染或更新之后被执行}
- setState
- forceUpdate
React.Children
React.Children 提供了用于处理 this.props.children 不透明数据结构的实用方法。不能保证props.children是数组,通过这种方式来实现遍历props.children
React.Children.map(props.children,()=>{})
规范
-
React 组件使用 PascalCase,组件实例和React DOM属性名称 使用 camelCase,属性值用单引号'value',当属性值为true时可以只写key
-
Refs避免使用字符串引用,请使用回调函数作为引用
<Foo
ref={ref => { this.myRef = ref }}
/>
-
建议尽量使用函数式组件配合 Hooks 来进行开发
-
class extends React.Component的顺序:
- 可选的 static 方法
- constructor
- getChildContext
- componentWillMount
- componentDidMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
- componentWillUnmount
- 事件处理函数 例如onClickSubmit()或onChangeDescription()
- render的getter方法 如getSelectReason() or getFooterContent()
- 可选的render方法 如 renderNavigation() or renderProfilePicture()
- render
React.createClass的顺序:
- eslint: react/sort-comp
- displayName
- propTypes
- contextTypes
- childContextTypes
- mixins
- statics
- defaultProps
- getDefaultProps
- getInitialState
- getChildContext
- componentWillMount
- componentDidMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
- componentWillUnmount
- 事件处理函数 如 onClickSubmit() 或 onChangeDescription()
- render的getter方法 如 getSelectReason() 或 getFooterContent()
- 可选的render方法 如 renderNavigation() 或 renderProfilePicture()
- render