如果你
⭐ 喜欢该项目,请单击。高度赞赏拉取请求。@SudheerJonna关注我以获取技术更新。
注意:此存储库特定于 ReactJS。请查看 Javascript 面试问题,了解核心 JavaScript 问题。
你可以从操作选项卡上的最新运行下载此存储库的 PDF 和 Epub 版本。
React 是一个开源的前端 JavaScript 库,用于构建用户界面,尤其是单页应用程序。它用于处理 Web 和移动应用程序的视图层。React是由为Facebook工作的软件工程师Jordan Walke创建的。React于2011年首次部署在Facebook的News Feed上,并于2012年部署在Instagram上。
React 的主要特性是:
JSX 是 ECMAScript 的类似 XML 的语法扩展(首字母缩略词代表 JavaScript XML)。基本上,它只是为函数提供语法糖,为我们提供JavaScript的表现力以及类似HTML的模板语法。
React.createElement()
在下面的示例中,标记内的文本作为 JavaScript 函数返回给渲染函数。
<h1>
class App extends React.Component {
render() {
return(
<div>
<h1>{'Welcome to React world!'}</h1>
</div>
)
}
}
元素是一个普通对象,描述你希望在屏幕上显示的 DOM 节点或其他组件的内容。元素可以在其道具中包含其他元素。创建一个 React 元素很便宜。一旦创建了元素,它就永远不会发生突变。
React Element 的对象表示如下:
const element = React.createElement(
'div',
{id: 'login-btn'},
'Login'
)
上面的函数返回一个对象:
React.createElement()
{ type: 'div', props: { children: 'Login', id: 'login-btn' } }
最后,它通过以下方式渲染到 DOM:
ReactDOM.render()
<div id='login-btn'>Login</div>
而组件可以通过几种不同的方式声明。它可以是带有方法的类,也可以定义为函数。无论哪种情况,它都将 props 作为输入,并返回一个 JSX 树作为输出:
render()
const Button = ({ onLogin }) =>
<div id={'login-btn'} onClick={onLogin}>Login</div>
然后 JSX 被转译为函数树:
React.createElement()
const Button = ({ onLogin }) => React.createElement(
'div',
{ id: 'login-btn', onClick: onLogin },
'Login'
)
有两种可能的方法可以创建组件。
功能组件:这是创建组件的最简单方法。这些是纯 JavaScript 函数,接受 props 对象作为第一个参数并返回 React 元素:
function Greeting({ message }) {
return <h1>{`Hello, ${message}`}</h1>
}
类组件:你还可以使用 ES6 类来定义组件。上面的函数组件可以写成:
class Greeting extends React.Component {
render() {
return <h1>{`Hello, ${this.props.message}`}</h1>
}
}
如果组件需要状态或生命周期方法,则使用类组件,否则使用函数组件。但是,从 React 16.8 开始,添加了 Hooks,你可以在函数组件中使用状态、生命周期方法和其他仅在类组件中可用的功能。*因此,始终建议使用函数组件,除非你需要一个 React 功能,其函数组件等效项尚不存在,例如错误边界 *
React.PureComponent
与 React.Component
完全相同,只是它为你处理该方法。当 props 或状态发生变化时,PureComponent 会对 props 和状态进行浅层比较。另一方面,组件不会将当前道具和状态与下一个开箱即用的状态进行比较。因此,默认情况下,每当调用组件时,组件都会重新呈现。
shouldComponentUpdate()
shouldComponentUpdate
组件的状态是一个对象,其中包含一些可能在组件生存期内更改的信息。我们应该始终尝试使我们的状态尽可能简单,并尽量减少有状态组件的数量。
让我们创建一个具有消息状态的用户组件,
class User extends React.Component {
constructor(props) {
super(props)
this.state = {
message: 'Welcome to React world'
}
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
</div>
)
}
}
状态类似于 props,但它是私有的并且完全由组件控制,即在所有者组件决定传递它之前,任何其他组件都无法访问它。
道具是组件的输入。它们是单个值或包含一组值的对象,这些值在创建时使用类似于 HTML 标记属性的命名约定传递给组件。它们是从父组件传递到子组件的数据。
React 中 props 的主要目的是提供以下组件功能:
this.props.reactProp
render()
例如,让我们创建一个带有属性的元素:
reactProp
<Element reactProp={'1'} />
然后,这个(或任何你想出的)名称成为附加到 React 原生 props 对象的属性,该对象最初已经存在于使用 React 库创建的所有组件上。
reactProp
props.reactProp
示例:基于类的组件中的道具
import React from 'react'
import ReactDOM from 'react-dom'
class ChildComponent extends React.Component {
render() {
return (
<div>
<p>{this.props.name}</p>
<p>{this.props.age}</p>
</div>
)
}
}
class ParentComponent extends React.Component {
render() {
return (
<div>
<ChildComponent name='John' age='30' />
<ChildComponent name='Mary' age='25' />
</div>
)
}
}
示例:功能组件中的道具
import React from 'react'
import ReactDOM from 'react-dom'
const ChildComponent = (props) => {
return (
<div>
<p>{props.name}</p>
<p>{props.age}</p>
</div>
)
}
const ParentComponent = () => {
return (
<div>
<ChildComponent name='John' age='30' />
<ChildComponent name='Mary' age='25' />
</div>
)
}
props 和 state 都是普通的 JavaScript 对象。虽然它们都包含影响渲染输出的信息,但它们在组件方面的功能不同。props 类似于函数参数传递给组件,而状态在组件内管理,类似于函数中声明的变量。
如果尝试直接更新状态,则不会重新呈现组件。
//Wrong
this.state.message = 'Hello world'
而是使用方法。它计划对组件的状态对象的更新。当状态更改时,组件会通过重新呈现来响应。
setState()
//Correct
this.setState({ message: 'Hello World' })
注意:你可以在构造函数中或使用最新的 javascript 的类字段声明语法直接分配给状态对象。
setState()
回调函数在 setState 完成并呈现组件时调用。由于是异步的,因此回调函数用于任何发布操作。
setState()
注意:建议使用生命周期方法,而不是此回调函数。
setState({ name: 'John' }, () => console.log('The name has updated and component re-rendered'))
以下是 HTML 和 React 事件处理之间的一些主要区别,
在 HTML 中,事件名称通常以小写形式表示为约定:
<button onclick='activateLasers()'>
而在 React 中,它遵循 camelCase 约定:
<button onClick={activateLasers}>
在 HTML 中,你可以返回以防止默认行为:
false
<a href='#' onclick='console.log("The link was clicked."); return false;' />
而在 React 中,你必须显式调用:
preventDefault()
function handleClick(event) {
event.preventDefault()
console.log('The link was clicked.')
}
在 HTML 中,你需要通过附加 T的方式来调用函数,而在 react 中,你不应该附加函数名称。(例如,请参阅第一点中的“激活激光”功能)
()
()
有 3 种可能的方法可以实现此目的:
构造函数中的绑定:在 JavaScript 类中,默认情况下不绑定这些方法。同样的事情也适用于定义为类方法的 React 事件处理程序。通常我们在构造函数中绑定它们。
class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
公共类字段语法:如果你不喜欢使用绑定方法,则可以使用公共类字段语法来正确绑定回调。
handleClick = () => {
console.log('this is:', this)
}
<button onClick={this.handleClick}>
{'Click me'}
</button>
回调中的箭头函数:可以直接在回调中使用箭头函数。
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={() => this.handleClick()}>Click Me</button>;
}
注意:如果回调作为 prop 传递给子组件,则这些组件可能会执行额外的重新渲染。在这些情况下,最好使用考虑性能的公共类字段语法方法。
.bind()
可以使用箭头函数环绕事件处理程序并传递参数:
<button onClick={() => this.handleClick(id)} />
这等效于调用:
.bind
<button onClick={this.handleClick.bind(this, id)} />
除了这两种方法之外,你还可以将参数传递给定义为箭头函数的函数
<button onClick={this.handleClick(id)} />
handleClick = (id) => () => {
console.log("Hello, your ticket number is", id)
};
SyntheticEvent是围绕浏览器本机事件的跨浏览器包装器。它的 API 与浏览器的本机事件相同,包括 和 ,只是这些事件在所有浏览器中的工作方式相同。
stopPropagation()
preventDefault()
你可以使用 if 语句或 JS 中可用的三元表达式来有条件地呈现表达式。除了这些方法之外,你还可以在 JSX 中嵌入任何表达式,方法是将它们括在大括号中,然后是 JS 逻辑运算符。
&&
<h1>Hello!</h1>
{
messages.length > 0 && !isLogin?
<h2>
You have {messages.length} unread messages.
</h2>
:
<h2>
You don't have unread messages.
</h2>
}
A 是创建元素数组时应包含的特殊字符串属性。键道具帮助 React 识别哪些项目已更改、添加或删除。
key
大多数情况下,我们使用数据中的 ID 作为键:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
)
当你没有渲染项的稳定 ID 时,可以使用项索引作为键作为最后的手段:
const todoItems = todos.map((todo, index) =>
<li key={index}>
{todo.text}
</li>
)
注意:
li
key
引用用于返回对元素的引用。在大多数情况下,应避免使用它们,但是,当你需要直接访问 DOM 元素或组件实例时,它们可能很有用。
有两种方法
这是最近添加的方法。引用是使用方法创建的,并通过属性附加到 React 元素。为了在整个组件中使用 ref,只需将 ref 分配给构造函数中的实例属性。
React.createRef()
ref
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return <div ref={this.myRef} />
}
}
你也可以使用 ref 回调方法,而不管 React 版本如何。例如,按如下方式访问搜索栏组件的输入元素,
class SearchBar extends Component {
constructor(props) {
super(props);
this.txtSearch = null;
this.state = { term: '' };
this.setInputSearchRef = e => {
this.txtSearch = e;
}
}
onInputChange(event) {
this.setState({ term: this.txtSearch.value });
}
render() {
return (
<input
value={this.state.term}
onChange={this.onInputChange.bind(this)}
ref={this.setInputSearchRef} />
);
}
}
你还可以使用闭包在函数组件中使用 refs。注意:你也可以使用内联引用回调,即使这不是推荐的方法。
引用转发是一项功能,它允许某些组件获取它们收到的引用,并将其进一步传递给子项。
const ButtonElement = React.forwardRef((props, ref) => (
<button ref={ref} className="CustomButton">
{props.children}
</button>
));
// Create ref to the DOM button:
const ref = React.createRef();
<ButtonElement ref={ref}>{'Forward Ref'}</ButtonElement>
最好使用回调引用而不是 API。因为会阻止将来 React 中的某些改进。
findDOMNode()
findDOMNode()
使用的传统方法:
findDOMNode
class MyComponent extends Component {
componentDidMount() {
findDOMNode(this).scrollIntoView()
}
render() {
return <div />
}
}
推荐的方法是:
class MyComponent extends Component {
constructor(props){
super(props);
this.node = createRef();
}
componentDidMount() {
this.node.current.scrollIntoView();
}
render() {
return <div ref={this.node} />
}
}
如果你以前使用过 React,你可能熟悉较旧的 API,其中属性是字符串,例如 ,并且 DOM 节点以 .我们建议不要这样做,因为字符串引用有以下问题,并且被认为是遗留的。字符串引用在 React v16 中删除。
ref
ref={'textInput'}
this.refs.textInput
this.refs
class MyComponent extends Component {
renderRow = (index) => {
// This won't work. Ref will get attached to DataTable rather than MyComponent:
return <input ref={'input-' + index} />;
// This would work though! Callback refs are awesome.
return <input ref={input => this['input-' + index] = input} />;
}
render() {
return <DataTable data={this.props.data} renderRow={this.renderRow} />
}
}
虚拟 DOM (VDOM) 是真实 DOM 的内存表示形式。UI 的表示形式保存在内存中,并与“真正的”DOM 同步。这是在调用的渲染函数和在屏幕上显示元素之间发生的步骤。整个过程称为和解。
虚拟 DOM 通过三个简单的步骤工作。
Shadow DOM 是一种浏览器技术,主要用于在 Web 组件中限定变量和 CSS 的作用域。虚拟 DOM 是由 JavaScript 中的库在浏览器 API 之上实现的概念。
Fiber 是 React v16 中核心算法的新协调引擎或重新实现。React Fiber 的目标是提高其对动画、布局、手势、暂停、中止或重用工作的能力以及为不同类型的更新分配优先级等领域的适用性;和新的并发原语。
React Fiber 的目标是提高其对动画、布局和手势等领域的适用性。它的主要功能是增量渲染:能够将渲染工作拆分为块并将其分布在多个帧上。
从文档
其主要目标是:
在后续用户输入中控制表单中的输入元素的组件称为受控组件,即每个状态突变都将具有关联的处理程序函数。
例如,要用大写字母写所有名称,我们使用handleChange,如下所示,
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()})
}
不受控制的组件是在内部存储自己的状态的组件,你可以使用 ref 查询 DOM 以在需要时查找其当前值。这有点像传统的HTML。
在下面的用户配置文件组件中,使用 ref 访问输入。
name
class UserProfile 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 type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
在大多数情况下,建议使用受控组件来实现表单。在受控组件中,表单数据由 React 组件处理。另一种方法是不受控制的组件,其中表单数据由 DOM 本身处理。
JSX 元素将被转译为函数以创建将用于 UI 对象表示的 React 元素。而 用于克隆元素并向其传递新道具。
React.createElement()
cloneElement
当多个组件需要共享相同的更改数据时,建议将共享状态提升到其最接近的共同祖先。这意味着,如果两个子组件共享其父组件的相同数据,则将状态移动到父组件,而不是在两个子组件中维护本地状态。
组件生命周期有三个不同的生命周期阶段:
安装:组件已准备好挂载到浏览器 DOM 中。此阶段涵盖从 、 、 和生命周期方法初始化。
constructor()
getDerivedStateFromProps()
render()
componentDidMount()
更新:在此阶段,组件以两种方式更新,发送新 props 并从 或 更新状态。此阶段涵盖 、 和生命周期方法。
setState()
forceUpdate()
getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
卸载:在最后一个阶段,不需要该组件,并从浏览器 DOM 中卸载。此阶段包括生命周期方法。
componentWillUnmount()
值得一提的是,React 内部在对 DOM 应用更改时有一个阶段的概念。它们分开如下
呈现该组件将呈现没有任何副作用。这适用于 Pure 组件,在此阶段,React 可以暂停、中止或重新启动渲染。
预提交在组件实际将更改应用于 DOM 之前,有一个时刻允许 React 通过 .
getSnapshotBeforeUpdate()
犯React 与 DOM 一起工作,并分别执行挂载、更新和卸载的最终生命周期。
componentDidMount()
componentDidUpdate()
componentWillUnmount()
React 16.3+ Phases(或交互式版本)
在 React 16.3 之前
在 React 16.3 之前
true
shouldComponentUpdate()
React 16.3+
render()
true
componentDidUpdate()
shouldComponentUpdate()
false
高阶组件 (HOC) 是获取组件并返回新组件的函数。基本上,这是一种源自 React 的合成性质的模式。
我们称它们为纯组件,因为它们可以接受任何动态提供的子组件,但它们不会修改或复制其输入组件中的任何行为。
const EnhancedComponent = higherOrderComponent(WrappedComponent)
HOC 可用于许多用例:
你可以使用 props 代理模式添加/编辑传递给组件的 props,如下所示:
function HOC(WrappedComponent) {
return class Test extends Component {
render() {
const newProps = {
title: 'New Header',
footer: false,
showFeatureX: false,
showFeatureY: true
}
return <WrappedComponent {...this.props} {...newProps} />
}
}
}
上下文提供了一种通过组件树传递数据的方法,而不必在每个级别手动传递 props。
例如,经过身份验证的用户、区域设置首选项、UI 主题需要由许多组件在应用程序中访问。
const {Provider, Consumer} = React.createContext(defaultValue)
Children 是一个 prop (),它允许你将组件作为数据传递给其他组件,就像你使用的任何其他 prop 一样。放在组件的开始和结束标记之间的组件树将作为 prop 传递给该组件。
this.props.children
children
React API 中有几种方法可以使用这个 prop。其中包括 、 、 、 。
React.Children.map
React.Children.forEach
React.Children.count
React.Children.only
React.Children.toArray
儿童道具的简单用法如下所示,
const MyDiv = React.createClass({
render: function() {
return <div>{this.props.children}</div>
}
})
ReactDOM.render(
<MyDiv>
<span>{'Hello'}</span>
<span>{'World'}</span>
</MyDiv>,
node
)
React/JSX 中的注释类似于 JavaScript 多行注释,但用大括号括起来。
单行注释:
<div>
{/* Single-line comments(In vanilla JavaScript, the single-line comments are represented by double slash(//)) */}
{`Welcome ${user}, let's play React`}
</div>
多行注释:
<div>
{/* Multi-line comments for more than
one line */}
{`Welcome ${user}, let's play React`}
</div>
在调用该方法之前,子类构造函数无法使用引用。这同样适用于 ES6 子类。将 props 参数传递给调用的主要原因是访问子构造函数。
this
super()
super()
this.props
传球道具:
class MyComponent extends React.Component {
constructor(props) {
super(props)
console.log(this.props) // prints { name: 'John', age: 42 }
}
}
不传递道具:
class MyComponent extends React.Component {
constructor(props) {
super()
console.log(this.props) // prints undefined
// but props parameter is still available
console.log(props) // prints { name: 'John', age: 42 }
}
render() {
// no difference outside constructor
console.log(this.props) // prints { name: 'John', age: 42 }
}
}
上面的代码片段揭示了仅在构造函数中有所不同。在构造函数外部也是如此。
this.props
当组件的 props 或状态发生变化时,React 通过将新返回的元素与之前渲染的元素进行比较来决定是否需要实际的 DOM 更新。当它们不相等时,React 将更新 DOM。此过程称为协调。
如果你使用 ES6 或 Babel 转译器来转换 JSX 代码,则可以使用计算的属性名称来完成此操作。
handleInputChange(event) {
this.setState({ [event.target.id]: event.target.value })
}
你需要确保在将函数作为参数传递时未调用该函数。
render() {
// Wrong: handleClick is called instead of passed as a reference!
return <button onClick={this.handleClick()}>{'Click Me'}</button>
}
相反,传递函数本身而不带括号:
render() {
// Correct: handleClick is passed as a reference!
return <button onClick={this.handleClick}>{'Click Me'}</button>
}
React.lazy
// MoreComponents.js
export const SomeComponent = /* ... */;
export const UnusedComponent = /* ... */;
MoreComponents.js
IntermediateComponent.js
// IntermediateComponent.js
export { SomeComponent as default } from "./MoreComponents.js";
import React, { lazy } from 'react';
const SomeComponent = lazy(() => import("./IntermediateComponent.js"));
className
class
class是 JavaScript 中的一个关键字,JSX 是 JavaScript 的扩展。这就是 React 使用而不是 .传递一个字符串作为道具。
className
class
className
render() {
return <span className={'menu navigation-menu'}>{'Menu'}</span>
}
这是 React 中的一种常见模式,用于组件返回多个元素。片段允许你对子节点列表进行分组,而无需向 DOM 添加额外的节点。
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
)
}
还有一个较短的语法,但许多工具不支持它:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
)
}
以下是原因列表,
门户是将子节点呈现到父组件的 DOM 层次结构之外的 DOM 节点的推荐方法。
ReactDOM.createPortal(child, container)
第一个参数是任何可渲染的 React 子参数,例如元素、字符串或片段。第二个参数是 DOM 元素。
如果组件的行为与其状态无关,那么它可以是无状态组件。可以使用函数或类来创建无状态组件。但是,除非你需要在组件中使用生命周期钩子,否则你应该选择函数组件。如果你决定在这里使用函数组件,有很多好处;它们易于编写、理解和测试,速度更快,你可以完全避免使用关键字。
this
如果组件的行为依赖于组件的状态,则可以将其称为有状态组件。这些有状态组件始终是类组件,并且具有在 中初始化的状态。
constructor
class App extends Component {
constructor(props) {
super(props)
this.state = { count: 0 }
}
render() {
// ...
}
}
React 16.8 更新:
钩子允许你使用状态和其他 React 功能,而无需编写类。
等效功能组件
import React, {useState} from 'react';
const App = (props) => {
const [count, setCount] = useState(0);
return (
// JSX
)
}
当应用程序在开发模式下运行时,React 会自动检查我们在组件上设置的所有 props,以确保它们具有正确的类型。如果类型不正确,React 将在控制台中生成警告消息。由于性能影响,它在生产模式下被禁用。强制道具由 定义。
isRequired
预定义道具类型的集合:
PropTypes.number
PropTypes.string
PropTypes.array
PropTypes.object
PropTypes.func
PropTypes.node
PropTypes.element
PropTypes.bool
PropTypes.symbol
PropTypes.any
我们可以为组件定义如下:
propTypes
User
import React from 'react'
import PropTypes from 'prop-types'
class User extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired
}
render() {
return (
<>
<h1>{`Welcome, ${this.props.name}`}</h1>
<h2>{`Age, ${this.props.age}`}</h2>
</>
)
}
}
注意:在 React v15.5 中,PropType 被移到了库中。
React.PropTypes
prop-types
等效功能组件
import React from 'react'
import PropTypes from 'prop-types'
function User({name, age}) {
return (
<>
<h1>{`Welcome, ${name}`}</h1>
<h2>{`Age, ${age}`}</h2>
</>
)
}
User.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired
}
以下是 React 的主要优点列表,
除了优点之外,React 也几乎没有限制,
错误边界是在其子组件树中的任何位置捕获 JavaScript 错误的组件,记录这些错误,并显示回退 UI,而不是崩溃的组件树。
如果类组件定义了一个名为 或 的新生命周期方法,则类组件将成为错误边界:
componentDidCatch(error, info)
static getDerivedStateFromError()
class ErrorBoundary extends React.Component {
constructor(props) {
super(props)
this.state = { hasError: false }
}
componentDidCatch(error, info) {
// You can also log the error to an error reporting service
logErrorToMyService(error, info)
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>{'Something went wrong.'}</h1>
}
return this.props.children
}
}
之后将其用作常规组件:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
React v15 使用方法为错误边界提供了非常基本的支持。它已在 React v16 中重命名。
unstable_handleError
componentDidCatch
通常我们使用 PropType 库(自 React v15.5 以来移至包)在 React 应用程序中进行类型检查。对于大型代码库,建议使用静态类型检查器(如 Flow 或 TypeScript),它们在编译时执行类型检查并提供自动完成功能。
React.PropTypes
prop-types
react-dom
该包提供了特定于 DOM 的方法,可在应用的顶层使用。大多数组件不需要使用此模块。此包的一些方法是:
react-dom
render()
hydrate()
unmountComponentAtNode()
findDOMNode()
createPortal()
react-dom
此方法用于将 React 元素呈现到提供的容器中的 DOM 中,并返回对该组件的引用。如果 React 元素之前被渲染到容器中,它将对其执行更新,并且仅在必要时改变 DOM 以反映最新的更改。
ReactDOM.render(element, container, [callback])
如果提供了可选回调,它将在组件渲染或更新后执行。
该对象使你能够将组件呈现为静态标记(通常在节点服务器上使用)。此对象主要用于服务器端渲染 (SSR)。以下方法可以在服务器和浏览器环境中使用:
ReactDOMServer
renderToString()
renderToStaticMarkup()
例如,你通常运行基于 Node 的 Web 服务器,如 Express、happy 或 Koa,然后调用以将根组件呈现为字符串,然后将其作为响应发送。
renderToString
// using Express
import { renderToString } from 'react-dom/server'
import MyPage from './MyPage'
app.get('/', (req, res) => {
res.write('<!DOCTYPE html><html><head><title>My Page</title></head><body>')
res.write('<div id="content">')
res.write(renderToString(<MyPage/>))
res.write('</div></body></html>')
res.end()
})
该属性是 React 在浏览器 DOM 中使用的替代品。就像 一样,考虑到跨站点脚本 (XSS) 攻击,使用此属性是有风险的。你只需要将对象作为键传递,将 HTML 文本作为值传递。
dangerouslySetInnerHTML
innerHTML
innerHTML
__html
在此示例中,MyComponent 使用属性来设置 HTML 标记:
dangerouslySetInnerHTML
function createMarkup() {
return { __html: 'First · Second' }
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />
}
该属性接受具有驼峰属性的 JavaScript 对象,而不是 CSS 字符串。这与 DOM 风格的 JavaScript 属性一致,效率更高,并防止 XSS 安全漏洞。
style
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')'
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>
}
样式键是驼峰化的,以便与访问 JavaScript 中 DOM 节点上的属性保持一致(例如)。
node.style.backgroundImage
在 React 元素中处理事件有一些语法差异:
setState()
当你使用 时,除了分配给对象状态之外,React 还会重新渲染组件及其所有子组件。你会收到如下错误:只能更新已装载或已装载的组件。所以我们需要使用 在构造函数中初始化变量。
setState()
this.state
键应该是稳定的、可预测的和唯一的,以便 React 可以跟踪元素。
在下面的代码片段中,每个元素的键将基于排序,而不是绑定到所表示的数据。这限制了 React 可以做的优化。
{todos.map((todo, index) =>
<Todo
{...todo}
key={index}
/>
)}
如果你使用元素数据作为唯一键,假设 todo.id 对于这个列表是唯一的并且稳定,React 将能够重新排序元素,而无需重新评估它们。
{todos.map((todo) =>
<Todo {...todo}
key={todo.id} />
)}
setState()
componentWillMount()
是的,在内部使用方法是安全的。但同时建议避免生命周期方法中的异步初始化。 在装载发生之前立即调用。它在 之前调用,因此在此方法中设置状态不会触发重新渲染。避免在此方法中引入任何副作用或订阅。我们需要确保在 中而不是 中发生组件初始化的异步调用。
setState()
componentWillMount()
componentWillMount()
componentWillMount()
render()
componentDidMount()
componentWillMount()
componentDidMount() {
axios.get(`api/todos`)
.then((result) => {
this.setState({
messages: [...result.data]
})
})
}
如果在未刷新组件的情况下更改组件上的 props,则永远不会显示新的 prop 值,因为构造函数永远不会更新组件的当前状态。来自 props 的状态初始化仅在首次创建组件时运行。
以下组件不会显示更新的输入值:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
records: [],
inputValue: this.props.inputValue
};
}
render() {
return <div>{this.state.inputValue}</div>
}
}
在渲染方法中使用 props 将更新值:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
record: []
}
}
render() {
return <div>{this.props.inputValue}</div>
}
}
在某些情况下,你希望根据某些状态呈现不同的组件。JSX 不呈现 或 ,因此仅当特定条件为真时,才可以使用条件短路来呈现组件的给定部分。
false
undefined
const MyComponent = ({ name, address }) => (
<div>
<h2>{name}</h2>
{address &&
<p>{address}</p>
}
</div>
)
如果需要条件,请使用三元运算符。
if-else
const MyComponent = ({ name, address }) => (
<div>
<h2>{name}</h2>
{address
? <p>{address}</p>
: <p>{'Address is not available'}</p>
}
</div>
)
当我们传播道具时,我们会遇到添加未知HTML属性的风险,这是一种不好的做法。相反,我们可以将 prop 解构与运算符一起使用,因此它将只添加所需的 prop。
...rest
例如
const ComponentA = () =>
<ComponentB isDisplay={true} className={'componentStyle'} />
const ComponentB = ({ isDisplay, ...domProps }) =>
<div {...domProps}>{'ComponentB'}</div>
你可以修饰类组件,这与将组件传递到函数中相同。装饰器是修改组件功能的灵活且可读的方式。
@setTitle('Profile')
class Profile extends React.Component {
//....
}
/*
title is a string that will be set as a document title
WrappedComponent is what our decorator will receive when
put directly above a component class as seen in the example above
*/
const setTitle = (title) => (WrappedComponent) => {
return class extends React.Component {
componentDidMount() {
document.title = title
}
render() {
return <WrappedComponent {...this.props} />
}
}
}
注意:装饰器是一个没有进入ES7的功能,但目前是第二阶段的提案。
有可用于函数组件的记忆库。
例如,库可以记忆另一个组件中的组件。
moize
import moize from 'moize'
import Component from './components/Component' // this module exports a non-memoized component
const MemoizedFoo = moize.react(Component)
const Consumer = () => {
<div>
{'I will memoize the following entry:'}
<MemoizedFoo/>
</div>
}
更新:从 React v16.6.0 开始,我们有一个 .它提供了一个高阶组件,除非道具发生变化,否则它会记住组件。要使用它,只需在使用它之前使用 React.memo 包装组件。
React.memo
const MemoComponent = React.memo(function MemoComponent(props) {
/* render using props */
});
OR
export default React.memo(MyFunctionComponent);
React 已经具备了处理 Node 服务器上渲染的能力。可以使用特殊版本的 DOM 渲染器,它遵循与客户端相同的模式。
import ReactDOMServer from 'react-dom/server'
import App from './App'
ReactDOMServer.renderToString(<App />)
此方法会将常规 HTML 输出为字符串,然后可以将其作为服务器响应的一部分放置在页面正文中。在客户端,React 会检测预渲染的内容,并无缝地从中断的地方继续。
你应该使用 Webpack 的方法设置为 ,通过这种方法,它会去除诸如 propType 验证和额外警告之类的内容。除此之外,如果你缩小代码,例如,Uglify的死代码消除以去除仅开发代码和注释,它将大大减少捆绑包的大小。
DefinePlugin
NODE_ENV
production
CLI 工具允许你快速创建和运行 React 应用程序,而无需配置步骤。
create-react-app
让我们使用 CRA 创建待办事项应用程序:
# Installation
$ npm install -g create-react-app
# Create new project
$ create-react-app todo-app
$ cd todo-app
# Build, test and run
$ npm run build
$ npm run test
$ npm start
它包含了我们构建 React 应用程序所需的一切:
创建组件实例并将其插入到 DOM 中时,将按以下顺序调用生命周期方法。
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
以下生命周期方法将是不安全的编码实践,并且异步渲染将带来更多问题。
componentWillMount()
componentWillReceiveProps()
componentWillUpdate()
从 React v16.3 开始,这些方法都带有前缀,无前缀的版本将在 React v17 中删除。
UNSAFE_
getDerivedStateFromProps()
新的静态生命周期方法在组件实例化之后以及重新呈现之前调用。它可以返回一个对象来更新状态,或者指示新 props 不需要任何状态更新。
getDerivedStateFromProps()
null
class MyComponent extends React.Component {
static getDerivedStateFromProps(props, state) {
// ...
}
}
此生命周期方法以及 涵盖了 的所有用例。
componentDidUpdate()
componentWillReceiveProps()
getSnapshotBeforeUpdate()
新的生命周期方法在 DOM 更新之前调用。此方法的返回值将作为第三个参数传递给 。
getSnapshotBeforeUpdate()
componentDidUpdate()
class MyComponent extends React.Component {
getSnapshotBeforeUpdate(prevProps, prevState) {
// ...
}
}
此生命周期方法以及 涵盖了 的所有用例。
componentDidUpdate()
componentWillUpdate()
渲染道具和高阶组件都只渲染一个子组件,但在大多数情况下,Hooks 是一种更简单的方法,可以通过减少树中的嵌套来提供这种服务。
建议通过引用而不是使用 .
displayName
用于命名组件:
displayName
export default React.createClass({
displayName: 'TodoApp',
// ...
})
推荐的方法:
export default class TodoApp extends React.Component {
// ...
}
也
const TodoApp = () => {
//...
}
export default TodoApp;
建议从挂载到渲染阶段的方法顺序:
static方法
constructor()
getChildContext()
componentWillMount()
componentDidMount()
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
componentDidUpdate()
componentWillUnmount()
onClickSubmit()
onChangeDescription()
getSelectReason()
getFooterContent()
renderNavigation()
renderProfilePicture()
render()
切换组件是呈现许多组件之一的组件。我们需要使用 object 将 prop 值映射到组件。
例如,一个切换组件,用于根据 prop 显示不同的页面:
page
import HomePage from './HomePage'
import AboutPage from './AboutPage'
import ServicesPage from './ServicesPage'
import ContactPage from './ContactPage'
const PAGES = {
home: HomePage,
about: AboutPage,
services: ServicesPage,
contact: ContactPage
}
const Page = (props) => {
const Handler = PAGES[props.page] || ContactPage
return <Handler {...props} />
}
// The keys of the PAGES object can be used in the prop types to catch dev-time errors.
Page.propTypes = {
page: PropTypes.oneOf(Object.keys(PAGES)).isRequired
}
其背后的原因是这是一个异步操作。React 批处理状态出于性能原因而更改,因此状态可能不会在调用后立即更改。这意味着在调用时不应依赖当前状态,因为你无法确定该状态是什么。解决方案是将函数传递给 ,并将以前的状态作为参数。通过这样做,可以避免由于 的异步性质而导致用户在访问时获取旧状态值的问题。
setState()
setState()
setState()
setState()
setState()
假设初始计数值为零。连续三次增量操作后,该值将仅递增 1。
// assuming this.state.count === 0
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
// this.state.count === 1, not 3
如果我们将一个函数传递给 ,计数将正确递增。
setState()
this.setState((prevState, props) => ({
count: prevState.count + props.increment
}))
// this.state.count === 3 as expected
(或)
setState()
React 可以将多个调用批处理到单个更新中以提高性能。由于 和 可能会异步更新,因此不应依赖它们的值来计算下一个状态。
setState()
this.props
this.state
此计数器示例将无法按预期更新:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
})
首选方法是使用函数而不是对象进行调用。该函数将接收以前的状态作为第一个参数,并将应用更新时的 props 作为第二个参数接收。
setState()
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}))
React.StrictMode是突出显示应用程序中潜在问题的有用组件。就像 一样,不渲染任何额外的 DOM 元素。它会为其后代激活其他检查和警告。这些检查仅适用于开发模式。
<Fragment>
<StrictMode>
import React from 'react'
function ExampleApplication() {
return (
<div>
<Header />
<React.StrictMode>
<div>
<ComponentOne />
<ComponentTwo />
</div>
</React.StrictMode>
<Header />
</div>
)
}
在上面的示例中,严格模式检查仅适用于 和 组件。
<ComponentOne>
<ComponentTwo>
Mixins 是一种将组件完全分离以具有通用功能的方法。不应使用混合,可以用更高阶的组件或装饰器代替。
最常用的混合之一是。你可能会在某些组件中使用它,以防止当 props 和状态与以前的 props 和状态浅相等时进行不必要的重新渲染:
PureRenderMixin
const PureRenderMixin = require('react-addons-pure-render-mixin')
const Button = React.createClass({
mixins: [PureRenderMixin],
// ...
})
isMounted()
主要用例是避免在卸载组件后调用,因为它会发出警告。
isMounted()
setState()
if (this.isMounted()) {
this.setState({...})
}
在调用之前进行检查确实会消除警告,但它也违背了警告的目的。使用 是一种代码异味,因为你要检查的唯一原因是你认为在组件卸载后你可能持有引用。
isMounted()
setState()
isMounted()
最佳解决方案是找到在组件卸载后可能调用的位置,并修复它们。这种情况最常由于回调而发生,当组件正在等待某些数据并在数据到达之前被卸载时。理想情况下,在卸载之前,应在 中取消任何回调。
setState()
componentWillUnmount()
指针事件提供处理所有输入事件的统一方法。在过去,我们有一个鼠标和各自的事件侦听器来处理它们,但现在我们有许多与鼠标无关的设备,例如带有触摸表面或笔的手机。我们需要记住,这些事件仅在支持指针事件规范的浏览器中有效。
以下事件类型现在在 React DOM 中可用:
onPointerDown
onPointerMove
onPointerUp
onPointerCancel
onGotPointerCapture
onLostPointerCapture
onPointerEnter
onPointerLeave
onPointerOver
onPointerOut
如果你使用 JSX 渲染你的组件,该组件的名称必须以大写字母开头,否则 React 将抛出错误作为无法识别的标签。此约定是因为只有 HTML 元素和 SVG 标记可以以小写字母开头。
class SomeComponent extends Component {
// Code goes here
}
你可以定义名称以小写字母开头的组件类,但在导入时应具有大写字母。这里小写很好:
class myComponent extends Component {
render() {
return <div />
}
}
export default myComponent
而在另一个文件中导入时,它应该以大写字母开头:
import MyComponent from './myComponent'
组件名称应以大写字母开头,但此约定很少有例外。带有点(属性访问器)的小写标记名称仍被视为有效的组件名称。例如,下面的标签可以编译成一个有效的组件,
render() {
return (
<obj.component/> // `React.createElement(obj.component)`
)
}
是的。过去,React 曾经忽略未知的 DOM 属性。如果你用 React 无法识别的属性编写 JSX,React 会跳过它。
例如,让我们看一下下面的属性:
<div mycustomattribute={'something'} />
将使用 React v15 向 DOM 渲染一个空的 div:
<div />
在 React v16 中,任何未知属性都将出现在 DOM 中:
<div mycustomattribute='something' />
这对于提供特定于浏览器的非标准属性、尝试新的 DOM API 以及与固执己见的第三方库集成非常有用。
使用 ES6 类时,应在构造函数中初始化状态,在使用 .
getInitialState()
React.createClass()
使用 ES6 类:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = { /* initial state */ }
}
}
使用 React.createClass():
const MyComponent = React.createClass({
getInitialState() {
return { /* initial state */ }
}
})
注意:在 React v16 中已弃用并删除。改用普通的 JavaScript 类。
React.createClass()
默认情况下,当组件的状态或 props 发生变化时,组件将重新渲染。如果你的方法依赖于其他一些数据,你可以通过调用 来告诉 React 组件需要重新渲染。
render()
forceUpdate()
component.forceUpdate(callback)
建议避免使用 和仅从 和 中读取。
forceUpdate()
this.props
this.state
render()
super()
super(props)
当你想进入时,你应该将道具传递给方法。
this.props
constructor()
super()
使用超级(道具):
class MyComponent extends React.Component {
constructor(props) {
super(props)
console.log(this.props) // { name: 'John', ... }
}
}
使用 super():
class MyComponent extends React.Component {
constructor(props) {
super()
console.log(this.props) // undefined
}
}
在两者之外将显示相同的值。
constructor()
this.props
你可以简单地使用 ES6 箭头函数语法。
Array.prototype.map
例如,对象数组映射到组件数组:
items
<tbody>
{items.map(item => <SomeComponent key={item.id} name={item.name} />)}
</tbody>
但是你不能使用循环进行迭代:
for
<tbody>
for (let i = 0; i < items.length; i++) {
<SomeComponent key={items[i].id} name={items[i].name} />
}
</tbody>
这是因为 JSX 标记被转译为函数调用,并且不能在表达式中使用语句。由于第一阶段提案的表达方式,这可能会改变。
do
React (或 JSX) 不支持属性值内的变量插值。以下表示法不起作用:
<img className='image' src='images/{this.props.image}' />
但是你可以将任何 JS 表达式作为整个属性值放在大括号内。所以下面的表达式有效:
<img className='image' src={'images/' + this.props.image} />
使用模板字符串也可以:
<img className='image' src={`images/${this.props.image}`} />
如果要将对象数组传递给具有特定形状的组件,请使用 的参数。
React.PropTypes.shape()
React.PropTypes.arrayOf()
ReactComponent.propTypes = {
arrayWithShape: React.PropTypes.arrayOf(React.PropTypes.shape({
color: React.PropTypes.string.isRequired,
fontSize: React.PropTypes.number.isRequired
})).isRequired
}
你不应该在引号内使用大括号,因为它将被计算为字符串。
<div className="btn-panel {this.props.visible ? 'show' : 'hidden'}">
相反,你需要将大括号移到外面(不要忘记在类名之间包含空格):
<div className={'btn-panel ' + (this.props.visible ? 'show' : 'hidden')}>
模板字符串也将起作用:
<div className={`btn-panel ${this.props.visible ? 'show' : 'hidden'}`}>
包包含 、、 以及与元素和组件类相关的其他帮助程序。你可以将这些视为构建组件所需的同构或通用帮助程序。该包包含 ,并且我们在服务器端渲染支持 和 .
react
React.createElement()
React.Component
React.Children
react-dom
ReactDOM.render()
react-dom/server
ReactDOMServer.renderToString()
ReactDOMServer.renderToStaticMarkup()
React 团队致力于将所有与 DOM 相关的功能提取到一个名为 ReactDOM 的独立库中。React v0.14 是第一个拆分库的版本。通过查看一些软件包,、、 和 ,很明显,React 的美感和本质与浏览器或 DOM 无关。
react-native
react-art
react-canvas
react-three
为了构建更多 React 可以渲染的环境,React 团队计划将主 React 包拆分为两个:和 .这为编写可以在 React 和 React Native 的 Web 版本之间共享的组件铺平了道路。
react
react-dom
如果尝试使用标准属性呈现绑定到文本输入的元素,则会生成缺少该属性的 HTML,并将警告打印到控制台。
<label>
for
<label for={'user'}>{'User'}</label>
<input type={'text'} id={'user'} />
由于是 JavaScript 中的保留关键字,请改用。
for
htmlFor
<label htmlFor={'user'}>{'User'}</label>
<input type={'text'} id={'user'} />
你可以在常规 React 中使用点差运算符:
<button style={{...styles.panel.button, ...styles.panel.submitButton}}>{'Submit'}</button>
如果你使用的是 React Native,那么你可以使用数组符号:
<button style={[styles.panel.button, styles.panel.submitButton]}>{'Submit'}</button>
你可以在 中侦听事件,然后更新维度 ( 和 )。你应该在方法中删除侦听器。
resize
componentDidMount()
width
height
componentWillUnmount()
class WindowDimensions extends React.Component {
constructor(props){
super(props);
this.updateDimensions = this.updateDimensions.bind(this);
}
componentWillMount() {
this.updateDimensions()
}
componentDidMount() {
window.addEventListener('resize', this.updateDimensions)
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateDimensions)
}
updateDimensions() {
this.setState({width: window.innerWidth, height: window.innerHeight})
}
render() {
return <span>{this.state.width} x {this.state.height}</span>
}
}
setState()
replaceState()
当你使用当前状态和以前的状态时,将合并。 抛弃当前状态,并仅将其替换为你提供的状态。通常使用,除非你出于某种原因确实需要删除所有以前的密钥。你还可以将状态设置为 / in 而不是使用 。
setState()
replaceState()
setState()
false
null
setState()
replaceState()
当状态更改时,将调用生命周期方法。你可以将提供的状态和 props 值与当前状态和 props 进行比较,以确定是否发生了有意义的更改。
componentDidUpdate
componentDidUpdate(object prevProps, object prevState)
注意:ReactJS的先前版本也用于状态更改。它在最新版本中已弃用。
componentWillUpdate(object nextProps, object nextState)
更好的方法是使用方法。
Array.prototype.filter()
例如,让我们创建一个用于更新状态的方法。
removeItem()
removeItem(index) {
this.setState({
data: this.state.data.filter((item, i) => i !== index)
})
}
最新版本(>=16.2)是可能的。以下是可能的选项:
render() {
return false
}
render() {
return null
}
render() {
return []
}
render() {
return <React.Fragment></React.Fragment>
}
render() {
return <></>
}
返回是行不通的。
undefined
我们可以使用标签来保留 的格式:
<pre>
JSON.stringify()
const data = { name: 'John', age: 42 }
class User extends React.Component {
render() {
return (
<pre>
{JSON.stringify(data, null, 2)}
</pre>
)
}
}
React.render(<User />, document.getElementById('container'))
React 的理念是道具应该是不可变的和自上而下的。这意味着父母可以将任何道具值发送给孩子,但孩子不能修改收到的道具。
你可以通过为元素创建 ref 并在 :
input
componentDidMount()
class App extends React.Component{
componentDidMount() {
this.nameInput.focus()
}
render() {
return (
<div>
<input
defaultValue={'Won\'t focus'}
/>
<input
ref={(input) => this.nameInput = input}
defaultValue={'Will focus'}
/>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
也在功能组件中( React 16.08 及以上)
import React, {useEffect, useRef} from 'react';
const App = () => {
const inputElRef = useRef(null)
useEffect(()=>{
inputElRef.current.focus()
}, [])
return(
<div>
<input
defaultValue={'Won\'t focus'}
/>
<input
ref={inputElRef}
defaultValue={'Will focus'}
/>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
调用 setState() 与要与状态合并的对象:
用于创建对象的副本:
Object.assign()
const user = Object.assign({}, this.state.user, { age: 42 })
this.setState({ user })
使用点差运算符:
const user = { ...this.state.user, age: 42 }
this.setState({ user })
使用函数调用 setState():
this.setState(prevState => ({
user: {
...prevState.user,
age: 42
}
}))
你可以使用 获取版本。
React.version
const REACT_VERSION = React.version
ReactDOM.render(
<div>{`React version: ${REACT_VERSION}`}</div>,
document.getElementById('app')
)
create-react-app
有一些方法可以在创建- React -应用程序中包含 polyfill,
从core-js手动导入:
创建一个名为(类似)的文件并将其导入根文件。运行或导入特定的必需功能。
polyfills.js
index.js
npm install core-js
yarn add core-js
import 'core-js/fn/array/find'
import 'core-js/fn/array/includes'
import 'core-js/fn/number/is-nan'
使用多边形填充服务:
使用 polyfill.io CDN 检索自定义的、特定于浏览器的 polyfill,方法是将此行添加到:
index.html
<script src='https://cdn.polyfill.io/v2/polyfill.min.js?features=default,Array.prototype.includes'></script>
在上面的脚本中,我们必须显式请求该功能,因为它不包含在默认功能集中。
Array.prototype.includes
你只需要使用配置。你可以编辑脚本部分:
HTTPS=true
package.json
"scripts": {
"start": "set HTTPS=true && react-scripts start"
}
或者只是运行
set HTTPS=true && npm start
创建一个在项目根目录中调用的文件并写入导入路径:
.env
NODE_PATH=src/app
之后重新启动开发服务器。现在,你应该能够在没有相对路径的情况下导入任何内容。
src/app
在对象上添加侦听器以记录每个页面视图:
history
history.listen(function (location) {
window.ga('set', 'page', location.pathname + location.search)
window.ga('send', 'pageview', location.pathname + location.search)
})
你需要使用 来触发更改,但你还需要在组件卸载时清除计时器,以防止错误和内存泄漏。
setInterval()
componentDidMount() {
this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000)
}
componentWillUnmount() {
clearInterval(this.interval)
}
React 不会自动应用供应商前缀。你需要手动添加供应商前缀。
<div style={{
transform: 'rotate(90deg)',
WebkitTransform: 'rotate(90deg)', // note the capital 'W' here
msTransform: 'rotate(90deg)' // 'ms' is the only lowercase vendor prefix
}} />
应使用默认值导出组件
import React from 'react'
import User from 'user'
export default class MyProfile extends React.Component {
render(){
return (
<User type="customer">
//...
</User>
)
}
}
使用导出说明符,MyProfile 将成为成员并导出到此模块,并且可以导入相同的内容,而无需在其他组件中提及名称。
React 的协调算法假设在没有任何相反信息的情况下,如果自定义组件在后续渲染中出现在同一位置,则它与以前的组件相同,因此重用以前的实例而不是创建新实例。
你可以使用 ES7 字段来定义常量。
static
class MyComponent extends React.Component {
static DEFAULT_PAGINATION = 10
}
可以使用 ref 属性通过回调获取对基础对象的引用,将引用存储为类属性,然后使用该引用稍后使用该方法触发事件处理程序的单击。
HTMLInputElement
HTMLElement.click
这可以通过两个步骤完成:
在渲染方法中创建引用:
<input ref={input => this.inputElement = input} />
在事件处理程序中应用 click 事件:
this.inputElement.click()
如果你想在 React 中使用 /,你将需要 Babel 和 transform-async-to-generator 插件。React Native 附带 Babel 和一组变换。
async
await
React 项目文件结构有两种常见的做法。
按要素或路径分组:
构建项目的一种常见方法是将 CSS、JS 和测试放在一起,按功能或路由分组。
common/ ├─ Avatar.js ├─ Avatar.css ├─ APIUtils.js └─ APIUtils.test.js feed/ ├─ index.js ├─ Feed.js ├─ Feed.css ├─ FeedStory.js ├─ FeedStory.test.js └─ FeedAPI.js profile/ ├─ index.js ├─ Profile.js ├─ ProfileHeader.js ├─ ProfileHeader.css └─ ProfileAPI.js
按文件类型分组:
构建项目的另一种流行方法是将类似的文件组合在一起。
api/ ├─ APIUtils.js ├─ APIUtils.test.js ├─ ProfileAPI.js └─ UserAPI.js components/ ├─ Avatar.js ├─ Avatar.css ├─ Feed.js ├─ Feed.css ├─ FeedStory.js ├─ FeedStory.test.js ├─ Profile.js ├─ ProfileHeader.js └─ ProfileHeader.css
React Transition Group 和 React Motion 是 React 生态系统中流行的动画包。
建议避免在组件中硬编码样式值。可能跨不同 UI 组件使用的任何值都应提取到其自己的模块中。
例如,可以将这些样式提取到单独的组件中:
export const colors = {
white,
black,
blue
}
export const space = [
0,
8,
16,
32,
64
]
然后在其他组件中单独导入:
import { space, colors } from './styles'
ESLint 是一种流行的 JavaScript linter。有一些插件可以分析特定的代码样式。React 最常见的一个是一个名为 的 npm 包。默认情况下,它将检查许多最佳实践,规则检查从迭代器中的键到一组完整的道具类型。
eslint-plugin-react
另一个流行的插件是 ,这将有助于解决可访问性的常见问题。由于JSX提供的语法与常规HTML略有不同,因此文本和问题(例如)不会被常规插件拾取。
eslint-plugin-jsx-a11y
alt
tabindex
你可以使用 AJAX 库,例如 Axios、jQuery AJAX 和浏览器内置的 .你应该在生命周期方法中获取数据。这样你就可以在检索数据时用于更新组件。
fetch
componentDidMount()
setState()
例如,从 API 获取的员工列表并设置本地状态:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
employees: [],
error: null
}
}
componentDidMount() {
fetch('https://api.example.com/items')
.then(res => res.json())
.then(
(result) => {
this.setState({
employees: result.employees
})
},
(error) => {
this.setState({ error })
}
)
}
render() {
const { error, employees } = this.state
if (error) {
return <div>Error: {error.message}</div>;
} else {
return (
<ul>
{employees.map(employee => (
<li key={employee.name}>
{employee.name}-{employee.experience}
</li>
))}
</ul>
)
}
}
}
渲染道具是一种简单的技术,用于使用值为函数的道具在组件之间共享代码。下面的组件使用返回 React 元素的渲染属性。
<DataProvider render={data => (
<h1>{`Hello ${data.target}`}</h1>
)}/>
像React Router和DownShift这样的库正在使用这种模式。
React Router 是一个建立在 React 之上的强大路由库,可帮助你以令人难以置信的速度添加新屏幕和流到你的应用程序中,同时保持 URL 与页面上显示的内容同步。
React Router 是库的包装器,它处理与浏览器及其浏览器和哈希历史记录的交互。它还提供内存历史记录,这对于没有全局历史记录的环境很有用,例如移动应用程序开发(React Native)和使用 Node 进行单元测试。
history
window.history
<Router>
React Router v4 提供以下 3 个组件:
<Router>
<BrowserRouter>
<HashRouter>
<MemoryRouter>
上述组件将创建浏览器、哈希和内存历史记录实例。React Router v4 使与路由器关联的实例的属性和方法通过对象中的上下文可用。
history
router
push()
replace()
history
历史记录实例有两种用于导航的方法。
push()
replace()
如果将历史记录视为访问过的位置的数组, 将向数组添加一个新位置,并将数组中的当前位置替换为新位置。
push()
replace()
有三种不同的方法可以在组件内实现编程路由/导航。
使用 withRouter() 高阶函数:
高阶函数会将历史对象作为组件的道具注入。此对象提供 和 方法来避免使用上下文。
withRouter()
push()
replace()
import { withRouter } from 'react-router-dom' // this also works with 'react-router-native'
const Button = withRouter(({ history }) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
{'Click Me!'}
</button>
))
使用 <Route> 组件和渲染道具模式:
该组件传递与 相同的 props,因此你将能够通过 history prop 访问 history 方法。
<Route>
withRouter()
import { Route } from 'react-router-dom'
const Button = () => (
<Route render={({ history }) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
{'Click Me!'}
</button>
)} />
)
使用上下文:
不建议使用此选项,并将其视为不稳定的 API。
const Button = (props, context) => (
<button
type='button'
onClick={() => {
context.history.push('/new-location')
}}
>
{'Click Me!'}
</button>
)
Button.contextTypes = {
history: React.PropTypes.shape({
push: React.PropTypes.func.isRequired
})
}
解析查询字符串的功能是从 React Router v4 中移除的,因为多年来一直有用户请求支持不同的实现。因此,已经决定用户选择他们喜欢的实现。建议的方法是使用查询字符串库。
const queryString = require('query-string');
const parsed = queryString.parse(props.location.search);
如果你想要一些原生的东西,你也可以使用:
URLSearchParams
const params = new URLSearchParams(props.location.search)
const foo = params.get('name')
应为 IE11 使用填充代码。
你必须将路由包装在一个块中,因为它是唯一的,因为它以独占方式呈现路由。
<Switch>
<Switch>
首先,你需要添加到导入中:
Switch
import { Switch, Router, Route } from 'react-router'
然后在块内定义路由:
<Switch>
<Router>
<Switch>
<Route {/* ... */} />
<Route {/* ... */} />
</Switch>
</Router>
history.push
导航时,你可以将道具传递给对象:
history
this.props.history.push({
pathname: '/template',
search: '?name=sudheer',
state: { detail: response.data }
})
该属性用于在方法中传递查询参数。
search
push()
A 呈现匹配的第一个子项。没有路径的 A 始终匹配。所以你只需要简单地删除路径属性,如下所示
<Switch>
<Route>
<Route>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/user" component={User}/>
<Route component={NotFound} />
</Switch>
以下是在 React Router v4 上获取历史对象的步骤列表,
创建一个导出对象的模块,并在整个项目中导入此模块。
history
例如,创建文件:
history.js
import { createBrowserHistory } from 'history'
export default createBrowserHistory({
/* pass a configuration object here if needed */
})
你应该使用该组件而不是内置路由器。导入上述内部文件:
<Router>
history.js
index.js
import { Router } from 'react-router-dom'
import history from './history'
import App from './App'
ReactDOM.render((
<Router history={history}>
<App />
</Router>
), holder)
你也可以使用类似于内置历史对象的推送方法:
history
// some-other-file.js
import history from './history'
history.push('/go-here')
该软件包在 React Router 中提供了组件。渲染将导航到新位置。与服务器端重定向一样,新位置将覆盖历史记录堆栈中的当前位置。
react-router
<Redirect>
<Redirect>
import React, { Component } from 'react'
import { Redirect } from 'react-router'
export default class LoginComponent extends Component {
render() {
if (this.state.isLoggedIn === true) {
return <Redirect to="/your/redirect/page" />
} else {
return <div>{'Login Please'}</div>
}
}
}
React Intl 库使 React 中的国际化变得简单明了,具有现成的组件和 API,可以处理从格式化字符串、日期和数字到复数的所有内容。React Intl 是 FormatJS 的一部分,它通过其组件和 API 提供与 React 的绑定。
以下是 React Intl 的主要功能,
该库提供了两种设置字符串、数字和日期格式的方法:
使用 react 组件:
<FormattedMessage
id={'account'}
defaultMessage={'The amount is less than minimum balance.'}
/>
使用 API:
const messages = defineMessages({
accountMessage: {
id: 'account',
defaultMessage: 'The amount is less than minimum balance.',
}
})
formatMessage(messages.accountMessage)
<FormattedMessage>
返回元素中的组件,而不是纯文本,因此它们不能用于占位符、替代文本等。在这种情况下,你应该使用 较低级别的 API .你可以使用高阶组件将对象注入到组件中,然后使用该对象上的 available 格式化消息。
<Formatted... />
react-intl
formatMessage()
intl
injectIntl()
formatMessage()
import React from 'react'
import { injectIntl, intlShape } from 'react-intl'
const MyComponent = ({ intl }) => {
const placeholder = intl.formatMessage({id: 'messageId'})
return <input placeholder={placeholder} />
}
MyComponent.propTypes = {
intl: intlShape.isRequired
}
export default injectIntl(MyComponent)
你可以使用以下命令获取应用程序的任何组件中的当前区域设置:
injectIntl()
import { injectIntl, intlShape } from 'react-intl'
const MyComponent = ({ intl }) => (
<div>{`The current locale is ${intl.locale}`}</div>
)
MyComponent.propTypes = {
intl: intlShape.isRequired
}
export default injectIntl(MyComponent)
高阶组件将允许你通过组件中的 props 访问该方法。该方法由 的实例在内部使用,并返回格式化日期的字符串表示形式。
injectIntl()
formatDate()
FormattedDate
import { injectIntl, intlShape } from 'react-intl'
const stringDate = this.props.intl.formatDate(date, {
year: 'numeric',
month: 'numeric',
day: 'numeric'
})
const MyComponent = ({intl}) => (
<div>{`The formatted date is ${stringDate}`}</div>
)
MyComponent.propTypes = {
intl: intlShape.isRequired
}
export default injectIntl(MyComponent)
浅表渲染对于在 React 中编写单元测试用例很有用。它允许你将组件呈现一个级别深,并断言有关其呈现方法返回的内容的事实,而无需担心未实例化或呈现的子组件的行为。
例如,如果你有以下组件:
function MyComponent() {
return (
<div>
<span className={'heading'}>{'Title'}</span>
<span className={'description'}>{'Description'}</span>
</div>
)
}
然后你可以断言如下:
import ShallowRenderer from 'react-test-renderer/shallow'
// in your test
const renderer = new ShallowRenderer()
renderer.render(<MyComponent />)
const result = renderer.getRenderOutput()
expect(result.type).toBe('div')
expect(result.props.children).toEqual([
<span className={'heading'}>{'Title'}</span>,
<span className={'description'}>{'Description'}</span>
])
TestRenderer
此包提供了一个渲染器,可用于将组件渲染为纯 JavaScript 对象,而无需依赖 DOM 或本机移动环境。这个包可以很容易地抓取由 ReactDOM 或 React Native 渲染的平台视图层次结构(类似于 DOM 树)的快照,而无需使用浏览器或 .
jsdom
import TestRenderer from 'react-test-renderer'
const Link = ({page, children}) => <a href={page}>{children}</a>
const testRenderer = TestRenderer.create(
<Link page={'https://www.facebook.com/'}>{'Facebook'}</Link>
)
console.log(testRenderer.toJSON())
// {
// type: 'a',
// props: { href: 'https://www.facebook.com/' },
// children: [ 'Facebook' ]
// }
ReactTestUtils 在包中提供,允许你针对模拟 DOM 执行操作以进行单元测试。
with-addons
Jest是Facebook基于Jasmine创建的JavaScript单元测试框架,并提供自动模拟创建和环境。它通常用于测试组件。
jsdom
与茉莉花相比,有几个优点:
jsdom
让我们为一个在文件中将两个数字相加的函数编写一个测试:
sum.js
const sum = (a, b) => a + b
export default sum
创建一个包含实际测试的文件名:
sum.test.js
import sum from './sum'
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3)
})
然后将以下部分添加到你的:
package.json
{
"scripts": {
"test": "jest"
}
}
最后,运行或和 Jest 将打印一个结果:
yarn test
npm test
$ yarn test
PASS ./sum.test.js
✓ adds 1 + 2 to equal 3 (2ms)
Flux 是一种应用程序设计范例,用于替代更传统的 MVC 模式。它不是一个框架或库,而是一种新型架构,补充了 React 和单向数据流的概念。Facebook 在使用 React 时在内部使用此模式。
调度程序、存储和查看具有不同输入和输出的组件之间的工作流如下所示:
Redux 是基于 Flux 设计模式的 JavaScript 应用程序的可预测状态容器。Redux 可以与 React 一起使用,也可以与任何其他视图库一起使用。它很小(约2kB),没有依赖关系。
Redux遵循三个基本原则:
与其说缺点,我们可以说使用Redux而不是Flux几乎没有妥协。具体如下:
redux-immutable-state-invariant
mapStateToProps()
mapDispatchToProps()
mapStateToProps()是一个实用程序,可帮助你的组件获取更新状态(由其他一些组件更新):
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
mapDispatchToProps()是一个实用程序,它将帮助你的组件触发操作事件(调度可能导致应用程序状态更改的操作):
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
建议始终对 使用“对象速记”形式。
mapDispatchToProps
Redux 将其包装在另一个看起来像 (...args) => dispatch(onTodoClick(...args)),并将该包装器函数作为道具传递给你的组件。
const mapDispatchToProps = ({
onTodoClick
})
在化简器中调度操作是一种反模式。你的化简器应该没有副作用,只需消化操作有效负载并返回新的状态对象。在化简器中添加侦听器和调度操作可能会导致链接操作和其他副作用。
你只需要使用 从创建存储的模块中导出存储。此外,它不应该污染全局窗口对象。
createStore()
store = createStore(myReducer)
export default store
这些库出于非常不同的目的而非常不同,但有一些模糊的相似之处。
Redux 是一种用于管理整个应用程序状态的工具。它通常用作 UI 的体系结构。把它想象成(一半)Angular的替代品。RxJS是一个响应式编程库。它通常用作在JavaScript中完成异步任务的工具。把它看作是承诺的替代品。Redux 使用响应式范例,因为存储是响应式的。商店从远处观察操作,并改变自己。RxJS也使用响应式范式,但它不是架构,而是为你提供基本的构建块, Observable 量,以实现此模式。
可以在方法中调度操作,也可以在方法中验证数据。
componentDidMount()
render()
class App extends Component {
componentDidMount() {
this.props.fetchData()
}
render() {
return this.props.isLoaded
? <div>{'Loaded'}</div>
: <div>{'Not Loaded'}</div>
}
}
const mapStateToProps = (state) => ({
isLoaded: state.isLoaded
})
const mapDispatchToProps = { fetchData }
export default connect(mapStateToProps, mapDispatchToProps)(App)
connect()
你需要执行两个步骤才能在容器中使用商店:
使用 mapStateToProps():
它将状态变量从你的商店映射到你指定的道具。
将上述道具连接到你的容器:函数返回的对象连接到容器。你可以从 导入。
mapStateToProps
connect()
react-redux
import React from 'react'
import { connect } from 'react-redux'
class App extends React.Component {
render() {
return <div>{this.props.containerData}</div>
}
}
function mapStateToProps(state) {
return { containerData: state.data }
}
export default connect(mapStateToProps)(App)
你需要在应用程序中编写一个根化简器,它将处理操作委托给由 生成的化简器。
combineReducers()
例如,让我们在操作后返回初始状态。众所周知,无论操作如何,当化简器作为第一个参数调用时,它们都应该返回初始状态。
rootReducer()
USER_LOGOUT
undefined
const appReducer = combineReducers({
/* your app's top-level reducers */
})
const rootReducer = (state, action) => {
if (action.type === 'USER_LOGOUT') {
state = undefined
}
return appReducer(state, action)
}
如果使用 ,你可能还需要清理你的存储空间。 在存储引擎中保留状态的副本。首先,你需要导入相应的存储引擎,然后在将其设置为 undefined 之前解析状态并清理每个存储状态键。
redux-persist
redux-persist
const appReducer = combineReducers({
/* your app's top-level reducers */
})
const rootReducer = (state, action) => {
if (action.type === 'USER_LOGOUT') {
Object.keys(state).forEach(key => {
storage.removeItem(`persist:${key}`)
})
state = undefined
}
return appReducer(state, action)
}
at
@ 符号实际上是一个用于表示装饰器的 JavaScript 表达式。通过修饰器,可以在设计时批注和修改类和属性。
让我们举一个在没有装饰器和没有装饰器的情况下设置 Redux 的例子。
不带装饰器:
import React from 'react'
import * as actionCreators from './actionCreators'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) }
}
class MyApp extends React.Component {
// ...define your main app here
}
export default connect(mapStateToProps, mapDispatchToProps)(MyApp)
带装饰器:
import React from 'react'
import * as actionCreators from './actionCreators'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) }
}
@connect(mapStateToProps, mapDispatchToProps)
export default class MyApp extends React.Component {
// ...define your main app here
}
上面的例子几乎相似,除了装饰器的用法。装饰器语法尚未内置到任何 JavaScript 运行时中,并且仍处于实验阶段,可能会发生变化。你可以使用 babel 作为装饰器支持。
你可以直接在应用程序中使用上下文,并且非常适合将数据传递到深度嵌套的组件,这是它的设计目的。
而 Redux 功能强大得多,并且提供了大量上下文 API 不提供的功能。此外,React Redux 在内部使用上下文,但它不会在公共 API 中公开这一事实。
化简器始终返回状态的累积(基于所有先前和当前操作)。因此,它们充当了状态的还原器。每次调用 Redux 化简器时,状态和操作都会作为参数传递。然后根据操作减少(或累加)此状态,然后返回下一个状态。你可以减少操作的集合和(存储的)初始状态,以对其执行这些操作以获取生成的最终状态。
你可以使用中间件来定义异步操作。
redux-thunk
让我们举一个使用提取 API 将特定帐户作为 AJAX 调用提取的示例:
export function fetchAccount(id) {
return dispatch => {
dispatch(setLoadingAccountState()) // Show a loading spinner
fetch(`/account/${id}`, (response) => {
dispatch(doneFetchingAccount()) // Hide loading spinner
if (response.status === 200) {
dispatch(setAccount(response.json)) // Use a normal function to set the received state
} else {
dispatch(someError)
}
})
}
}
function setAccount(data) {
return { type: 'SET_Account', data: data }
}
将数据保留在 Redux 存储中,并将 UI 相关状态保留在组件内部。
在组件中访问商店的最佳方法是使用该函数,该函数创建一个围绕现有组件的新组件。这种模式被称为高阶组件,通常是在 React 中扩展组件功能的首选方式。这允许你将状态和操作创建者映射到组件,并在商店更新时自动传入它们。
connect()
让我们以使用 connect 的组件为例:
<FilterLink>
import { connect } from 'react-redux'
import { setVisibilityFilter } from '../actions'
import Link from '../components/Link'
const mapStateToProps = (state, ownProps) => ({
active: ownProps.filter === state.visibilityFilter
})
const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
})
const FilterLink = connect(
mapStateToProps,
mapDispatchToProps
)(Link)
export default FilterLink
由于它具有相当多的性能优化并且通常不太可能导致错误,因此Redux开发人员几乎总是建议使用直接访问商店(使用上下文API)。
connect()
class MyComponent {
someMethod() {
doSomethingWith(this.context.store)
}
}
组件是描述应用程序的表示部分的类或函数组件。
容器是连接到 Redux 存储的组件的非正式术语。容器订阅 Redux 状态更新和调度操作,它们通常不渲染 DOM 元素;它们将呈现委托给表示子组件。
使用常量,你可以在使用 IDE 时在整个项目中轻松找到该特定功能的所有用法。它还可以防止你引入由拼写错误引起的愚蠢错误——在这种情况下,你将立即得到一个。
ReferenceError
通常我们会将它们保存在单个文件( 或 )。
constants.js
actionTypes.js
export const ADD_TODO = 'ADD_TODO'
export const DELETE_TODO = 'DELETE_TODO'
export const EDIT_TODO = 'EDIT_TODO'
export const COMPLETE_TODO = 'COMPLETE_TODO'
export const COMPLETE_ALL = 'COMPLETE_ALL'
export const CLEAR_COMPLETED = 'CLEAR_COMPLETED'
在 Redux 中,你可以在两个位置使用它们:
在操作创建期间:
让我们采取:
actions.js
import { ADD_TODO } from './actionTypes';
export function addTodo(text) {
return { type: ADD_TODO, text }
}
在减速机中:
让我们创建:
reducer.js
import { ADD_TODO } from './actionTypes'
export default (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false
}
];
default:
return state
}
}
mapDispatchToProps()
有几种方法可以将动作创建者绑定到 中。
dispatch()
mapDispatchToProps()
以下是可能的选项:
const mapDispatchToProps = (dispatch) => ({
action: () => dispatch(action())
})
const mapDispatchToProps = (dispatch) => ({
action: bindActionCreators(action, dispatch)
})
const mapDispatchToProps = { action }
第三个选项只是第一个选项的简写。
ownProps
mapStateToProps()
mapDispatchToProps()
如果指定了参数,React Redux 会将传递给组件的 props 传递给你的连接函数。因此,如果你使用连接的组件:
ownProps
import ConnectedComponent from './containers/ConnectedComponent';
<ConnectedComponent user={'john'} />
你的和函数内部将是一个对象:
ownProps
mapStateToProps()
mapDispatchToProps()
{ user: 'john' }
可以使用此对象来决定从这些函数返回的内容。
大多数应用程序都有几个顶级目录,如下所示:
此结构适用于中小型应用。
redux-saga是一个库,旨在使 React/Redux 应用程序中的副作用(异步事情,如数据获取和不纯的东西,如访问浏览器缓存)更容易和更好。
它在 NPM 中可用:
$ npm install --save redux-saga
Saga 就像应用程序中的一个单独线程,它全权负责副作用。 是一个 Redux 中间件,这意味着这个线程可以通过正常的 Redux 操作从主应用程序中启动、暂停和取消,它可以访问完整的 Redux 应用程序状态,也可以调度 Redux 操作。
redux-saga
call()
put()
两者都是效果创建器函数。 函数用于创建效果描述,指示中间件调用 promise。 函数创建一个效果,指示中间件将操作调度到存储区。
call()
put()
call()
put()
让我们举例说明这些效果如何用于获取特定用户数据。
function* fetchUserSaga(action) {
// `call` function accepts rest arguments, which will be passed to `api.fetchUser` function.
// Instructing middleware to call promise, it resolved value will be assigned to `userData` variable
const userData = yield call(api.fetchUser, action.userId)
// Instructing middleware to dispatch corresponding action.
yield put({
type: 'FETCH_USER_SUCCESS',
userData
})
}
Redux Thunk 中间件允许你编写返回函数而不是操作的操作创建者。thunk 可用于延迟操作的调度,或仅在满足特定条件时才调度。内部函数接收存储方法和参数。
dispatch()
getState()
redux-saga
redux-thunk
Redux Thunk和Redux Saga都负责处理副作用。在大多数情况下,Thunk使用Promises来处理它们,而Saga使用生成器。Thunk使用简单,许多开发人员都熟悉Promises,Sagas/Generators功能更强大,但你需要学习它们。但是这两种中间件可以共存,因此你可以从Thunks开始,并在需要时引入Sagas。
Redux DevTools 是 Redux 的实时编辑时间旅行环境,具有热重载、动作回放和可自定义的 UI。如果你不想费心安装 Redux DevTools 并将其集成到你的项目中,请考虑使用 Chrome 和 Firefox 的 Redux DevTools Extension。
Redux DevTools 的一些主要功能如下:
persistState()
选择器是将 Redux 状态作为参数并返回一些数据以传递给组件的函数。
例如,要从状态获取用户详细信息,请执行以下操作:
const getUserData = state => state.user.data
这些选择器有两个主要优点,
Redux Form 与 React 和 Redux 配合使用,使 React 中的表单能够使用 Redux 来存储其所有状态。Redux Form可以与原始HTML5输入一起使用,但它也可以很好地与常见的UI框架一起使用,如Material UI,React Widgets和React Bootstrap。
Redux Form的一些主要功能是:
你可以使用 .
applyMiddleware()
例如,你可以将它们作为参数添加并传递给:
redux-thunk
logger
applyMiddleware()
import { createStore, applyMiddleware } from 'redux'
const createStoreWithMiddleware = applyMiddleware(ReduxThunk, logger)(createStore)
你需要将初始状态作为第二个参数传递给 createStore:
const rootReducer = combineReducers({
todos: todos,
visibilityFilter: visibilityFilter
})
const initialState = {
todos: [{ id: 123, name: 'example', completed: false }]
}
const store = createStore(
rootReducer,
initialState
)
Relay 与 Redux 类似,因为它们都使用单个存储。主要区别在于 relay 只管理源自服务器的状态,所有对状态的访问都通过 GraphQL 查询(用于读取数据)和突变(用于更改数据)使用。Relay 为你缓存数据并为你优化数据获取,仅获取更改的数据,仅提取其他数据。
操作是普通的 JavaScript 对象或信息的有效负载,用于将数据从应用程序发送到你的商店。它们是商店的唯一信息来源。操作必须具有指示正在执行的操作类型的类型属性。
例如,让我们执行一个表示添加新待办事项的操作:
{ type: ADD_TODO, text: 'Add todo item' }
React 是一个 JavaScript 库,支持前端 Web 并在服务器上运行,用于构建用户界面和 Web 应用程序。
React Native 是一个编译为原生应用程序组件的移动框架,允许你在 JavaScript 中构建原生移动应用程序(iOS、Android 和 Windows),允许你使用 React 构建组件,并在后台实现 React。
React Native 只能在 iOS 和 Android 等移动模拟器中进行测试。你可以使用世博会应用程序(https://expo.io)在手机中运行该应用程序,如果它使用QR码同步,则你的手机和计算机应位于同一无线网络中。
你可以使用 、 等。从 React Native v0.29 开始,你只需运行以下命令即可在控制台中查看日志:
console.log
console.warn
$ react-native log-ios $ react-native log-android
按照以下步骤调试 React Native 应用程序:
Command + D
http://localhost:8081/debugger-ui
Command + Option + I
View
Developer
Developer Tools
Reselect是一个使用记忆概念的选择器库(用于Redux)。它最初是为了计算来自类似 Redux 的应用程序状态的派生数据而编写的,但它不能绑定到任何体系结构或库。
重新选择保留上次调用的上次输入/输出的副本,并且仅在其中一个输入更改时才重新计算结果。如果连续两次提供相同的输入,则“重新选择”将返回缓存的输出。它的记忆和缓存是完全可定制的。
Flow 是一个静态类型检查器,旨在查找 JavaScript 中的类型错误。与传统类型系统相比,流动类型可以表达更细粒度的区别。例如,Flow 可帮助你捕获涉及 的错误,这与大多数类型系统不同。
null
Flow 是一个静态分析工具(静态检查器),它使用语言的超集,允许你向所有代码添加类型注释,并在编译时捕获整类错误。
PropType 是一个基本的类型检查器(运行时检查器),它已经修补到 React 上。除了传递给给定组件的 props 类型之外,它无法检查任何其他内容。如果你想对整个项目进行更灵活的类型检查,Flow/TypeScript 是合适的选择。
以下步骤在 React 中包含 Font Awesome:
安装:
font-awesome
$ npm install --save font-awesome
在文件中导入:
font-awesome
index.js
import 'font-awesome/css/font-awesome.min.css'
在以下位置添加字体 awesome 类:
className
render() {
return <div><i className={'fa fa-spinner'} /></div>
}
React 开发人员工具允许你检查组件层次结构,包括组件属性和状态。它既可以作为浏览器扩展(适用于Chrome和Firefox)存在,也可以作为独立应用程序(适用于其他环境,包括Safari,IE和React Native)存在。
适用于不同浏览器或环境的官方扩展。
如果你在浏览器中打开了本地 HTML 文件(),则必须先打开 Chrome 扩展程序并检查 。
file://...
Allow access to file URLs
你需要按照以下步骤在 React 中使用 Polymer,
创建聚合物元素:
<link rel='import' href='../../bower_components/polymer/polymer.html' />
Polymer({
is: 'calender-element',
ready: function() {
this.textContent = 'I am a calender'
}
})
通过将 Polymer 组件 HTML 标签导入到 HTML 文档中来创建它,例如将其导入 React 应用程序:
index.html
<link rel='import' href='./src/polymer-components/calender-element.html'>
在 JSX 文件中使用该元素:
import React from 'react'
class MyComponent extends React.Component {
render() {
return (
<calender-element />
)
}
}
export default MyComponent
与 Vue.js相比,React 具有以下优势:
注意:上述优势列表纯粹是固执己见,根据专业经验而有所不同。但它们作为基本参数很有用。
让我们看看表格格式的 React 和 Angular 之间的区别。
React | 角 |
---|---|
React 是一个库,只有视图层 | Angular是一个框架,具有完整的MVC功能 |
React 处理服务器端的渲染 | AngularJS仅在客户端渲染,但Angular 2及更高版本在服务器端渲染 |
React使用JSX,看起来像JS中的HTML,这可能会令人困惑 | Angular 遵循 HTML 的模板方法,这使得代码更短且易于理解 |
React Native,这是一种用于构建移动应用程序的React类型,更快,更稳定 | Ionic,Angular的移动原生应用程序相对不太稳定且速度较慢 |
在 React 中,数据仅以一种方式流动,因此调试很容易 | 在 Angular 中,数据是双向流动的,即它在子项和父项之间具有双向数据绑定,因此调试通常很困难 |
注意:上述差异列表纯粹是固执己见,根据专业经验而有所不同。但它们作为基本参数很有用。
当页面加载时,React DevTools 会设置一个名为 的全局,然后 React 在初始化期间与该钩子通信。如果网站没有使用 React 或者 React 无法与 DevTools 通信,那么它就不会显示该选项卡。
__REACT_DEVTOOLS_GLOBAL_HOOK__
styled-components是一个用于样式化 React 应用程序的 JavaScript 库。它删除了样式和组件之间的映射,并允许你编写使用 JavaScript 增强的实际 CSS。
让我们为每个组件创建和具有特定样式的组件。
<Title>
<Wrapper>
import React from 'react'
import styled from 'styled-components'
// Create a <Title> component that renders an <h1> which is centered, red and sized at 1.5em
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`
// Create a <Wrapper> component that renders a <section> with some padding and a papayawhip background
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`
这两个变量和 现在是可以像任何其他 react 组件一样渲染的组件。
Title
Wrapper
<Wrapper>
<Title>{'Lets start first styled component!'}</Title>
</Wrapper>
Relay 是一个 JavaScript 框架,用于使用 React 视图层为 Web 应用程序提供数据层和客户端-服务器通信。
create-react-app
从 react-scripts@2.1.0 或更高版本开始,对打字稿有内置支持。即,现在本机支持打字稿。你可以按如下方式传递选项
create-react-app
--typescript
npx create-react-app my-app --typescript
# or
yarn create react-app my-app --typescript
但是对于较低版本的 react 脚本,只需在创建新项目时提供选项即可。 是一组调整,用于采用标准项目管道并将 TypeScript 引入组合。
--scripts-version
react-scripts-ts
react-scripts-ts
create-react-app
现在,项目布局应如下所示:
my-app/ ├─ .gitignore ├─ images.d.ts ├─ node_modules/ ├─ public/ ├─ src/ │ └─ ... ├─ package.json ├─ tsconfig.json ├─ tsconfig.prod.json ├─ tsconfig.test.json └─ tslint.json
让我们看看重新选择库的主要功能,
让我们通过简化的 Reselect 用法计算货件订单的不同数量:
import { createSelector } from 'reselect'
const shopItemsSelector = state => state.shop.items
const taxPercentSelector = state => state.shop.taxPercent
const subtotalSelector = createSelector(
shopItemsSelector,
items => items.reduce((acc, item) => acc + item.value, 0)
)
const taxSelector = createSelector(
subtotalSelector,
taxPercentSelector,
(subtotal, taxPercent) => subtotal * (taxPercent / 100)
)
export const totalSelector = createSelector(
subtotalSelector,
taxSelector,
(subtotal, tax) => ({ total: subtotal + tax })
)
let exampleState = {
shop: {
taxPercent: 8,
items: [
{ name: 'apple', value: 1.20 },
{ name: 'orange', value: 0.95 },
]
}
}
console.log(subtotalSelector(exampleState)) // 2.15
console.log(taxSelector(exampleState)) // 0.172
console.log(totalSelector(exampleState)) // { total: 2.322 }
否,仅适用于:
statics
React.createClass()
someComponent= React.createClass({
statics: {
someMethod: function() {
// ..
}
}
})
但是你可以在 ES6+ 类中编写静态,如下所示:
class Component extends React.Component {
static propTypes = {
// ...
}
static someMethod() {
// ...
}
}
或在课外写如下,
class Component extends React.Component {
....
}
Component.propTypes = {...}
Component.someMethod = function(){....}
Redux 可以用作任何 UI 层的数据存储。最常见的用法是 React 和 React Native,但也有可用于 Angular、Angular 2、Vue、Mithril 等的绑定。Redux 只是提供了一种订阅机制,可供任何其他代码使用。
Redux 最初是用 ES6 编写的,后来使用 Webpack 和 Babel 转译为 ES5。无论你的 JavaScript 构建过程如何,你都应该能够使用它。Redux 还提供了一个 UMD 构建,可以直接使用,而无需任何构建过程。
initialValues
你需要添加设置。
enableReinitialize : true
const InitializeFromStateForm = reduxForm({
form: 'initializeFromState',
enableReinitialize : true
})(UserEdit)
如果你的道具更新,你的表单也会更新。
initialValues
你可以使用 的方法。
oneOfType()
PropTypes
例如,可以使用 或 类型定义 height 属性,如下所示:
string
number
Component.propTypes = {
size: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
])
}
你可以直接将 SVG 作为组件导入,而不是将其作为文件加载。此功能适用于及更高版本。
react-scripts@2.0.0
import { ReactComponent as Logo } from './logo.svg'
const App = () => (
<div>
{/* Logo is an actual react component */}
<Logo />
</div>
)
注意:不要忘记导入中的大括号。
如果 ref 回调被定义为内联函数,它将在更新期间被调用两次,首先使用 null,然后使用 DOM 元素。这是因为每次渲染都会创建一个函数的新实例,因此 React 需要清除旧的 ref 并设置新的 ref。
class UserForm extends Component {
handleSubmit = () => {
console.log("Input Value is: ", this.input.value)
}
render () {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
ref={(input) => this.input = input} /> // Access DOM input in handle submit
<button type='submit'>Submit</button>
</form>
)
}
}
但我们的期望是在组件挂载时调用一次 ref 回调。一种快速解决方法是使用 ES7 类属性语法来定义函数
class UserForm extends Component {
handleSubmit = () => {
console.log("Input Value is: ", this.input.value)
}
setSearchInput = (input) => {
this.input = input
}
render () {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
ref={this.setSearchInput} /> // Access DOM input in handle submit
<button type='submit'>Submit</button>
</form>
)
}
}
**Note:** In React v16.3,
渲染劫持的概念是控制一个组件将从另一个组件输出的内容的能力。这意味着你可以通过将组件包装到高阶组件中来装饰组件。通过包装,你可以注入其他道具或进行其他更改,这可能会导致渲染逻辑发生变化。它实际上并不启用劫持,但通过使用 HOC 可以使组件的行为不同。
在 React 中实现 HOC 有两种主要方法。
但是它们遵循不同的方法来操作包装组件。
道具代理
在这种方法中,HOC 的呈现方法返回一个包装组件类型的 React 元素。我们还通过 HOC 接收的道具,因此得名道具代理。
function ppHOC(WrappedComponent) {
return class PP extends React.Component {
render() {
return <WrappedComponent {...this.props}/>
}
}
}
继承反转
在此方法中,返回的 HOC 类(增强器)扩展了包装组件。它被称为继承反转,因为它不是 WrappedComponent 扩展某个增强器类,而是由增强器被动扩展。这样看来,它们之间的关系是相反的。
function iiHOC(WrappedComponent) {
return class Enhancer extends WrappedComponent {
render() {
return super.render()
}
}
}
你应该通过大括号 ({}) 传递数字,其中作为引号中的字符串
React.render(<User age={30} department={"IT"} />, document.getElementById('container'));
这取决于开发人员的决定,即开发人员的工作是确定哪种状态构成你的应用程序,以及每个状态应该位于何处。一些用户更喜欢将每一条数据都保存在 Redux 中,以便始终维护其应用程序的完全可序列化和受控版本。其他人更喜欢将非关键或 UI 状态(例如“此下拉列表当前是否打开”)保留在组件的内部状态中。
以下是确定应将哪种数据放入 Redux 的经验法则
默认情况下,React 会为你创建一个服务工作线程,无需任何配置。服务工作者是一个 Web API,可帮助你缓存资产和其他文件,以便当用户离线或在慢速网络上时,他/她仍然可以在屏幕上看到结果,因此,它可以帮助你构建更好的用户体验,这就是你现在应该了解的关于服务工作者的信息。这一切都是为了向你的网站添加离线功能。
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
当类组件的输入属性相同时,可以使用 PureComponent 或 shouldComponentUpdate 来限制类组件重新渲染。现在,你可以通过将函数组件包装在 React.memo 中来对它们执行相同的操作。
const MyComponent = React.memo(function MyComponent(props) {
/* only rerenders if props change */
});
React.lazy
OtherComponent
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<OtherComponent />
</div>
);
}
React.lazy
Suspense
你可以将状态的当前值与现有状态值进行比较,并决定是否重新呈现页面。如果值相同,则需要返回 null 以停止重新渲染,否则返回最新的状态值。
例如,用户配置文件信息有条件地呈现如下,
getUserProfile = user => {
const latestAddress = user.address;
this.setState(state => {
if (state.address === latestAddress) {
return null;
} else {
return { title: latestAddress };
}
});
};
数组:与旧版本不同,你不需要确保渲染方法在 React16 中返回单个元素。你可以通过返回数组来返回多个没有包装元素的同级元素。
例如,让我们以下面的开发人员列表为例,
const ReactJSDevs = () => {
return [
<li key="1">John</li>,
<li key="2">Jackie</li>,
<li key="3">Jordan</li>
];
}
你还可以将此项目数组合并到另一个数组组件中。
const JSDevs = () => {
return (
<ul>
<li>Brad</li>
<li>Brodge</li>
<ReactJSDevs/>
<li>Brandon</li>
</ul>
);
}
字符串和数字:还可以从 render 方法返回字符串和数字类型。
render() {
return 'Welcome to ReactJS questions';
}
// Number
render() {
return 2018;
}
React 类组件可以使用类字段声明变得更加简洁。可以在不使用构造函数的情况下初始化本地状态,并使用箭头函数声明类方法,而无需绑定它们。
让我们举一个反例来演示不使用构造函数和不绑定的方法的状态的类字段声明,
class Counter extends Component {
state = { value: 0 };
handleIncrement = () => {
this.setState(prevState => ({
value: prevState.value + 1
}));
};
handleDecrement = () => {
this.setState(prevState => ({
value: prevState.value - 1
}));
};
render() {
return (
<div>
{this.state.value}
<button onClick={this.handleIncrement}>+</button>
<button onClick={this.handleDecrement}>-</button>
</div>
)
}
}
Hooks 是一个特殊的函数(在 React 16.8 中作为新功能引入),它允许你在不编写类的情况下使用状态和其他 React 功能。
让我们看一个useState钩子的例子:
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
注意:钩子可以在现有函数组件中使用。
你需要遵循两个规则才能使用钩子,
npm install eslint-plugin-react-hooks@next
// Your ESLint configuration
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error"
}
}
以下是 Flux 和 Redux 之间的主要区别
通量 | 雷杜克斯 |
---|---|
状态是可变的 | 状态是不可变的 |
应用商店包含状态和更改逻辑 | 存储和更改逻辑是分开的 |
存在多个商店 | 只有一家商店存在 |
所有商店都是断开和扁平的 | 带分层缩减器的单一商店 |
它有一个单例调度程序 | 没有调度程序的概念 |
React 组件订阅商店 | 容器组件使用连接功能 |
以下是 React Router V4 模块的主要优点,
<BrowserRouter>
<Route>
<BrowserRouter>
组件 DidCatch 生命周期方法在后代组件引发错误后调用。该方法接收两个参数,
方法结构如下
componentDidCatch(error, info)
以下是错误边界不起作用的情况,
错误边界不会捕获事件处理程序中的错误。
React 不需要错误边界来从事件处理程序中的错误中恢复。与呈现方法和生命周期方法不同,事件处理程序在呈现期间不会发生。因此,如果他们抛出,React 仍然知道在屏幕上显示什么。
如果需要在事件处理程序中捕获错误,请使用常规的 JavaScript try / catch 语句:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
try {
// Do something that could throw
} catch (error) {
this.setState({ error });
}
}
render() {
if (this.state.error) {
return <h1>Caught an error.</h1>
}
return <button onClick={this.handleClick}>Click Me</button>
}
}
请注意,上面的示例演示了常规的 JavaScript 行为,并且不使用错误边界。
尝试 catch 块适用于命令性代码,而错误边界用于在屏幕上呈现的声明性代码。
例如,用于以下命令式代码的 try catch 块
try {
showButton();
} catch (error) {
// ...
}
虽然错误边界包装声明性代码,如下所示,
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
因此,如果 componentDidUpdate 方法中发生错误,由树中某处的 setState 引起,它仍将正确传播到最近的错误边界。
除了错误消息和 javascript 堆栈之外,React16 还将使用错误边界概念显示带有文件名和行号的组件堆栈跟踪。
例如,BuggyCounter 组件显示组件堆栈跟踪,如下所示,
render()
以下是以下使用和从渲染方法返回的类型列表,
<div/>
构造函数主要用于两个目的,
constructor(props) {
super(props);
// Don't call this.setState() here!
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
defaultProps 被定义为组件类上的一个属性,用于设置该类的默认 props。这用于未定义的道具,但不用于空道具。
例如,让我们为按钮组件创建颜色默认道具,
class MyButton extends React.Component {
// ...
}
MyButton.defaultProps = {
color: 'red'
};
如果未提供,则它将默认值设置为“红色”。即,每当你尝试访问颜色道具时,它都会使用默认值
props.color
render() {
return <MyButton /> ; // props.color will be set to red
}
注意:如果提供空值,则它仍然是空值。
setState()
componentWillUnmount()
此生命周期方法在后代组件引发错误后调用。它接收作为参数引发的错误,并应返回一个值以更新状态。
生命周期方法的签名如下:
static getDerivedStateFromError(error)
让我们以上述生命周期方法的错误边界用例为例进行演示,
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
更新可能是由对道具或状态的更改引起的。重新呈现组件时,将按以下顺序调用以下方法。
当呈现期间、生命周期方法或任何子组件的构造函数中出现错误时,将调用以下方法。
显示名称字符串用于调试消息。通常,不需要显式设置它,因为它是从定义组件的函数或类的名称推断出来的。如果要显示其他名称以进行调试或在创建高阶组件时,可能需要显式设置它。
例如,为了便于调试,请选择一个显示名称,以传达它是订阅 HOC 的结果。
function withSubscription(WrappedComponent) {
class WithSubscription extends React.Component {/* ... */}
WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`;
return WithSubscription;
}
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
此方法可从 react-dom 包中获得,它从 DOM 中删除一个挂载的 React 组件并清理其事件处理程序和状态。如果容器中未装载任何组件,则调用此函数不执行任何操作。如果组件已卸载,则返回 true;如果没有要卸载的组件,则返回 false。
方法签名如下所示,
ReactDOM.unmountComponentAtNode(container)
代码拆分是Webpack和Browserify等捆绑器支持的功能,它可以创建多个可以在运行时动态加载的捆绑包。react 项目支持通过动态 import() 功能进行代码拆分。
例如,在下面的代码片段中,它将使 moduleA.js 及其所有唯一依赖项作为一个单独的块,仅在用户单击“加载”按钮后加载。模块 A.js
const moduleA = 'Hello';
export { moduleA };
应用.js
import React, { Component } from 'react';
class App extends Component {
handleClick = () => {
import('./moduleA')
.then(({ moduleA }) => {
// Use moduleA
})
.catch(err => {
// Handle failure
});
};
render() {
return (
<div>
<button onClick={this.handleClick}>Load</button>
</div>
);
}
}
export default App;
在以下情况下会有所帮助
使用显式<React.Fragment>语法声明的片段可能有键。一般用例是将集合映射到片段数组,如下所示,
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
// Without the `key`, React will fire a key warning
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</React.Fragment>
))}
</dl>
);
}
注意:key 是唯一可以传递给片段的属性。将来,可能会支持其他属性,例如事件处理程序。
从 React 16 开始,标准或自定义 DOM 属性都完全受支持。由于 React 组件通常同时采用自定义和与 DOM 相关的 props,因此 React 像 DOM API 一样使用 camelCase 约定。
让我们对标准 HTML 属性采取一些道具,
<div tabIndex="-1" /> // Just like node.tabIndex DOM API
<div className="Button" /> // Just like node.className DOM API
<input readOnly={true} /> // Just like node.readOnly DOM API
这些 props 的工作方式与相应的 HTML 属性类似,但特殊情况除外。它还支持所有 SVG 属性。
除了优点之外,高阶组件还有一些注意事项。以下是按顺序列出的几个,
不要在呈现方法中使用 HOC:不建议将 HOC 应用于组件的呈现方法中的组件。
render() {
// A new version of EnhancedComponent is created on every render
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// That causes the entire subtree to unmount/remount each time!
return <EnhancedComponent />;
}
上面的代码通过重新装载组件来影响性能,该组件会导致该组件及其所有子组件的状态丢失。相反,请在组件定义之外应用 HOC,以便仅创建一次生成的组件。
静态方法必须复制到:将 HOC 应用于组件时,新组件不具有原始组件的任何静态方法
// Define a static method
WrappedComponent.staticMethod = function() {/*...*/}
// Now apply a HOC
const EnhancedComponent = enhance(WrappedComponent);
// The enhanced component has no static method
typeof EnhancedComponent.staticMethod === 'undefined' // true
你可以通过在返回容器之前将方法复制到容器上来解决此问题,
function enhance(WrappedComponent) {
class Enhance extends React.Component {/*...*/}
// Must know exactly which method(s) to copy :(
Enhance.staticMethod = WrappedComponent.staticMethod;
return Enhance;
}
引用不会传递:对于 HOC,你需要将所有道具传递到包装的组件,但这不适用于 refs。这是因为 ref 并不是真正类似于键的道具。在这种情况下,你需要使用 React.forwardRef API
React.forwardRef 接受渲染函数作为参数,DevTools 使用此函数来确定要为 ref 转发组件显示的内容。
例如,如果不命名渲染函数或不使用 displayName 属性,则它将在 DevTools 中显示为“ForwardRef”,
const WrappedComponent = React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
但是如果你命名渲染函数,那么它将显示为“ForwardRef(myFunction)”
const WrappedComponent = React.forwardRef(
function myFunction(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
);
作为替代方法,你还可以为 forwardRef 函数设置 displayName 属性,
function logProps(Component) {
class LogProps extends React.Component {
// ...
}
function forwardRef(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
// Give this component a more helpful display name in DevTools.
// e.g. "ForwardRef(logProps(MyComponent))"
const name = Component.displayName || Component.name;
forwardRef.displayName = `logProps(${name})`;
return React.forwardRef(forwardRef);
}
如果未传递任何 prop 的值,则默认为 true。此行为可用,以便它与 HTML 的行为匹配。
例如,下面的表达式是等效的,
<MyInput autocomplete />
<MyInput autocomplete={true} />
注意:不建议使用此方法,因为它可能会与 ES6 对象速记混淆(示例,它是
{name}
{name: name})
Next.js 是一个流行的轻量级框架,用于使用 React 构建的静态和服务器渲染应用程序。它还提供样式和路由解决方案。以下是NextJS提供的主要功能,
可以将事件处理程序和其他函数作为 props 传递给子组件。它可以在子组件中使用,如下所示,
<button onClick={this.handleClick}>
是的,你可以使用。这通常是将参数传递给回调函数的最简单方法。但是你需要在使用它时优化性能。
class Foo extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={() => this.handleClick()}>Click Me</button>;
}
}
注意:在 render 方法中使用箭头函数会在每次组件呈现时创建一个新函数,这可能会对性能产生影响
如果使用事件处理程序(如 onClick 或 onScroll),并且希望防止回调触发得太快,则可以限制执行回调的速率。这可以通过以下可能的方式实现,
React DOM 在渲染之前转义了嵌入在 JSX 中的任何值。因此,它确保永远无法注入应用程序中未显式编写的任何内容。所有内容在呈现之前都转换为字符串。
例如,你可以嵌入用户输入,如下所示,
const name = response.potentiallyMaliciousInput;
const element = <h1>{name}</h1>;
通过这种方式,你可以防止应用程序中的XSS(跨站点脚本)攻击。
你可以通过将新创建的元素传递给 ReactDOM 的渲染方法来更新 UI(由渲染元素表示)。
例如,让我们以滴答作响的时钟为例,它通过多次调用 render 方法来更新时间,
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
当你将组件声明为函数或类时,它绝不能修改自己的 props。
让我们取一个下面的大写函数,
function capital(amount, interest) {
return amount + interest;
}
上述函数称为“pure”,因为它不会尝试更改其输入,并且始终为相同的输入返回相同的结果。因此,React 有一个规则,说“所有 React 组件在 props 方面必须像纯函数一样运行。
当你在组件中调用 setState() 时,React 会将你提供的对象合并到当前状态中。
例如,让我们将带有帖子和评论详细信息的 Facebook 用户作为状态变量,
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
现在,你可以使用单独的调用单独更新它们,如下所示,
setState()
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
如上面的代码片段所述,仅更新注释变量,而不修改或替换变量。
this.setState({comments})
posts
在迭代或循环期间,通常会将额外的参数传递给事件处理程序。这可以通过箭头函数或绑定方法实现。
让我们以网格中更新的用户详细信息为例,
<button onClick={(e) => this.updateUser(userId, e)}>Update User details</button>
<button onClick={this.updateUser.bind(this, userId)}>Update User details</button>
在这两种方法中,合成参数作为第二个参数传递。你需要为箭头函数显式传递它,并且它将自动传递给方法。
e
bind
你可以通过根据特定条件返回 null 来阻止组件呈现。这样,它可以有条件地呈现组件。
function Greeting(props) {
if (!props.loggedIn) {
return null;
}
return (
<div className="greeting">
welcome, {props.name}
</div>
);
}
class User extends React.Component {
constructor(props) {
super(props);
this.state = {loggedIn: false, name: 'John'};
}
render() {
return (
<div>
//Prevent component render if it is not loggedIn
<Greeting loggedIn={this.state.loggedIn} />
<UserDetails name={this.state.name}>
</div>
);
}
在上面的示例中,组件通过应用条件并返回 null 值来跳过其呈现部分。
greeting
有三个条件可以确保,使用索引作为键是安全的。
数组中使用的键在其同级中应该是唯一的,但它们不需要是全局唯一的。即,你可以将相同的键用于两个不同的数组。
例如,下面的组件使用两个具有不同数组的数组,
Book
function Book(props) {
const index = (
<ul>
{props.pages.map((page) =>
<li key={page.id}>
{page.title}
</li>
)}
</ul>
);
const content = props.pages.map((page) =>
<div key={page.id}>
<h3>{page.title}</h3>
<p>{page.content}</p>
<p>{page.pageNumber}</p>
</div>
);
return (
<div>
{index}
<hr />
{content}
</div>
);
}
Formik是 react 的表单库,它提供了验证、跟踪访问过的字段和处理表单提交等解决方案。
详细地,你可以按以下方式对它们进行分类,
它用于创建一个可扩展的、高性能的表单助手,具有最小的 API 来解决烦人的事情。
以下是推荐 formik 而不是 redux 表单库的主要原因,
是的,你可以在 react 应用程序中使用 Web 组件。尽管许多开发人员不会使用此组合,但如果你使用的是使用 Web 组件编写的第三方 UI 组件,则可能需要这样做。
例如,让我们使用日期选择器 Web 组件,如下所示,
Vaadin
import React, { Component } from 'react';
import './App.css';
import '@vaadin/vaadin-date-picker';
class App extends Component {
render() {
return (
<div className="App">
<vaadin-date-picker label="When were you born?"></vaadin-date-picker>
</div>
);
}
}
export default App;
你可以使用动态导入在应用中实现代码拆分。
让我们举一个加法的例子,
import { add } from './math';
console.log(add(10, 20));
import("./math").then(math => {
console.log(math.add(10, 20));
});
如果要在服务器渲染的应用程序中执行代码拆分,建议使用可加载组件,因为 React.lazy 和 Suspense 尚不可用于服务器端渲染。可加载允许你将动态导入呈现为常规组件。
让我们举个例子,
import loadable from '@loadable/component'
const OtherComponent = loadable(() => import('./OtherComponent'))
function MyComponent() {
return (
<div>
<OtherComponent />
</div>
)
}
现在,其他组件将加载到一个单独的捆绑包中
如果在父组件呈现时尚未加载包含动态导入的模块,则必须在使用加载指示器等待加载时显示一些回退内容。这可以使用悬念组件来完成。
例如,下面的代码使用悬念组件,
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
如上面的代码所述,悬念被包裹在惰性组件之上。
进行代码拆分的最佳位置之一是使用路由。整个页面将立即重新呈现,因此用户不太可能同时与页面中的其他元素进行交互。因此,用户体验不会受到干扰。
让我们举一个基于路由的网站的例子,使用像 React Router 这样的库和 React.lazy,
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspense, lazy } from 'react';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Suspense>
</Router>
);
在上面的代码中,代码拆分将发生在每个路由级别。
上下文旨在共享可以被视为 React 组件树的全局数据。
例如,在下面的代码中,让我们手动线程化“主题”道具,以便设置按钮组件的样式。
//Lets create a context with a default theme value "luna"
const ThemeContext = React.createContext('luna');
// Create App component where it uses provider to pass theme value in the tree
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value="nova">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// A middle component where you don't need to pass theme prop anymore
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
// Lets read theme value in the button component to use
class ThemedButton extends React.Component {
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
仅当组件在树中没有匹配的提供程序时,才使用 defaultValue 参数。这对于在不包装组件的情况下单独测试组件很有帮助。
下面的代码片段提供默认主题值为 Luna。
const MyContext = React.createContext(defaultValue);
上下文类型用于使用上下文对象。contextType 属性可以通过两种方式使用,
上下文类型作为类的属性:类上的 contextType 属性可以分配一个由 React.createContext() 创建的 Context 对象。之后,你可以在任何生命周期方法和渲染函数中使用 this.context 使用该上下文类型的最接近的当前值。
让我们在 MyClass 上分配 contextType 属性,如下所示:
class MyClass extends React.Component {
componentDidMount() {
let value = this.context;
/* perform a side-effect at mount using the value of MyContext */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* render something based on the value of MyContext */
}
}
MyClass.contextType = MyContext;
静态字段你可以使用静态类字段通过公共类字段语法初始化上下文类型。
class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
/* render something based on the value */
}
}
消费者是订阅上下文更改的 React 组件。它需要一个函数作为子函数,该函数接收当前上下文值作为参数并返回一个 React 节点。传递给函数的 value 参数将等于树中上面此上下文的最接近的提供程序的值属性。
让我们举一个简单的例子,
<MyContext.Consumer>
{value => /* render something based on the context value */}
</MyContext.Consumer>
上下文使用引用标识来确定何时重新呈现,当提供程序的父级重新呈现时,有一些陷阱可能会在使用者中触发意外呈现。
例如,每次提供程序重新呈现时,下面的代码都会重新呈现所有使用者,因为始终会为值创建一个新对象。
class App extends React.Component {
render() {
return (
<Provider value={{something: 'something'}}>
<Toolbar />
</Provider>
);
}
}
这可以通过将值提升到父状态来解决,
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}
引用不会通过,因为 ref 不是道具。React 以不同的方式处理它,就像键一样。如果将 ref 添加到 HOC,则 ref 将引用最外层的容器组件,而不是包装的组件。在这种情况下,你可以使用前向引用 API。例如,我们可以使用 React.forwardRef API 显式地将 refs 转发到内部 FancyButton 组件。
下面的 HOC 记录所有道具,
function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
const {forwardedRef, ...rest} = this.props;
// Assign the custom prop "forwardedRef" as a ref
return <Component ref={forwardedRef} {...rest} />;
}
}
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}
让我们使用这个 HOC 来记录传递给我们的“花哨按钮”组件的所有道具,
class FancyButton extends React.Component {
focus() {
// ...
}
// ...
}
export default logProps(FancyButton);
现在让我们创建一个 ref 并将其传递给 FancyButton 组件。在这种情况下,你可以将焦点设置为按钮元素。
import FancyButton from './FancyButton';
const ref = React.createRef();
ref.current.focus();
<FancyButton
label="Click Me"
handleClick={handleClick}
ref={ref}
/>;
如果你不使用 ES6,那么你可能需要改用 create-react-class 模块。对于默认 props,你需要将 getDefaultProps() 定义为传递对象上的函数。而对于初始状态,你必须提供一个单独的 getInitialState 方法来返回初始状态。
var Greeting = createReactClass({
getDefaultProps: function() {
return {
name: 'Jhohn'
};
},
getInitialState: function() {
return {message: this.props.message};
},
handleClick: function() {
console.log(this.state.message);
},
render: function() {
return <h1>Hello, {this.props.name}</h1>;
}
});
注意:如果使用 createReactClass,则自动绑定可用于所有方法。即,你不需要对事件处理程序使用 with in 构造函数。
.bind(this)
是的,JSX 对于使用 React 不是强制性的。实际上,当你不想在构建环境中设置编译时,这很方便。每个 JSX 元素都只是用于调用的语法糖。
React.createElement(component, props, ...children)
例如,让我们以 JSX 的问候语为例,
class Greeting extends React.Component {
render() {
return <div>Hello {this.props.message}</div>;
}
}
ReactDOM.render(
<Greeting message="World" />,
document.getElementById('root')
);
你可以在没有 JSX 的情况下编写相同的代码,如下所示:
class Greeting extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.message}`);
}
}
ReactDOM.render(
React.createElement(Greeting, {message: 'World'}, null),
document.getElementById('root')
);
React 需要使用算法来找出如何有效地更新 UI 以匹配最新的树。差异算法正在生成将一棵树转换为另一棵树的最小操作数。但是,这些算法的复杂性为 O(n³),其中 n 是树中元素的数量。
在这种情况下,显示 1000 个元素将需要大约 10 亿次比较。这太贵了。相反,React 基于两个假设实现了启发式 O(n) 算法:
当比较两个树时,React 首先比较两个根元素。行为因根元素的类型而异。它涵盖了对账算法期间的以下规则,
<div className="show" title="ReactJS" />
<div className="hide" title="ReactJS" />
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
很少有用例可以参考,
即使名为 render props 的模式,你也不必使用名为 render 的 prop 来使用此模式。即,任何作为组件用来知道要渲染什么的功能的道具在技术上都是“渲染道具”。让我们举一个渲染道具的孩子道具的例子,
<Mouse children={mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}/>
实际上,子属性不需要在JSX元素的“属性”列表中命名。相反,你可以将其直接保留在元素中,
<Mouse>
{mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}
</Mouse>
在使用上述技术(没有任何名称)时,明确声明子项应该是 propType 中的一个函数。
Mouse.propTypes = {
children: PropTypes.func.isRequired
};
你可以使用带有渲染道具的常规组件实现大多数高阶组件 (HOC)。例如,如果你希望使用带有鼠标 HOC 而不是组件,则可以使用带有渲染道具的常规轻松创建一个组件。
function withMouse(Component) {
return class extends React.Component {
render() {
return (
<Mouse render={mouse => (
<Component {...this.props} mouse={mouse} />
)}/>
);
}
}
}
通过这种方式,渲染道具提供了使用任一模式的灵活性。
假值(如假值、空值、未定义值和真值)是有效的子值,但它们不会呈现任何内容。如果你仍然想显示它们,则需要将其转换为字符串。让我们举一个关于如何转换为字符串的示例,
<div>
My JavaScript variable is {String(myVariable)}.
</div>
当父组件溢出时,React 门户非常有用:隐藏或具有影响堆叠上下文的属性(例如 z-index、位置、不透明度),并且你需要直观地“突破”其容器。
例如,对话框、全局消息通知、悬停卡和工具提示。
在 React 中,表单元素上的 value 属性将覆盖 DOM 中的值。对于不受控制的组件,你可能希望 React 指定初始值,但保持后续更新不受控制。若要处理这种情况,可以指定默认值属性而不是值。
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
User Name:
<input
defaultValue="John"
type="text"
ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
这同样适用于和输入。但是你需要使用默认检查和输入。
select
textArea
checkbox
radio
以下是Real DOM和Virtual DOM之间的主要区别,
真正的DOM | 虚拟 DOM |
---|---|
更新速度很慢 | 更新速度很快 |
DOM 操作非常昂贵。 | DOM 操作非常简单 |
你可以直接更新 HTML。 | 你无法直接更新 HTML |
它会导致太多的内存浪费 | 没有内存浪费 |
在元素更新时创建新的 DOM | 如果元素更新,它会更新 JSX |
引导程序可以通过三种可能的方式添加到你的 React 应用程序中,
npm install bootstrap
下面是使用 React 作为他们的前端框架,
top 10 websites
调用的效果钩子用于使用 axios 从 API 获取数据,并使用状态钩子的更新函数将数据设置为组件的本地状态。
useEffect
让我们举一个例子,它从 API 获取 React 文章列表
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [data, setData] = useState({ hits: [] });
useEffect(() => {
(async () => {
const result = await axios(
'http://hn.algolia.com/api/v1/search?query=react',
);
setData(result.data);
})()
}, []);
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
请记住,我们提供了一个空数组作为效果钩子的第二个参数,以避免在组件更新时激活它,而仅在组件挂载时激活它。即,它仅在组件安装上获取。
React 在 16.8 版本中为以下软件包提供了 React Hooks 的稳定实现
useState
当我们用 声明一个状态变量时,它返回一对 — 一个包含两个项目的数组。第一项是当前值,第二项是更新值的函数。使用 [0] 和 [1] 访问它们有点令人困惑,因为它们具有特定的含义。这就是我们使用数组解构的原因。
useState
例如,数组索引访问如下所示:
var userStateVariable = useState('userProfile'); // Returns an array pair
var user = userStateVariable[0]; // Access first item
var setUser = userStateVariable[1]; // Access second item
而通过数组解构,可以按如下方式访问变量:
const [user, setUser] = useState('userProfile');
胡克斯从几个不同的来源得到了这些想法。以下是其中的一些,
Formik 是一个小型的 React 表单库,可帮助你解决三个主要问题,
Redux Thunk, Redux Promise, Redux Saga
react-scripts
react-scripts start
以下是创建 React 应用程序提供的一些功能的列表。
ReactDOMServer#renderToNodeStream
npm install mobx --save
npm install mobx-react --save
Below are the main differences between Redux and MobX,
Topic | Redux | MobX |
---|---|---|
Definition | It is a javascript library for managing the application state | It is a library for reactively managing the state of your applications |
Programming | It is mainly written in ES6 | It is written in JavaScript(ES5) |
Data Store | There is only one large store exist for data storage | There is more than one store for storage |
Usage | Mainly used for large and complex applications | Used for simple applications |
Performance | Need to be improved | Provides better performance |
How it stores | Uses JS Object to store | Uses observable to store the data |
No, you don’t have to learn es2015/es6 to learn react. But you may find many resources or React ecosystem uses ES6 extensively. Let's see some of the frequently used ES6 features,
// in es 5
var someData = this.props.someData
var dispatch = this.props.dispatch
// in es6
const { someData, dispatch } = this.props
// in es 5
<SomeComponent someData={this.props.someData} dispatch={this.props.dispatch} />
// in es6
<SomeComponent {...this.props} />
// es 5
var users = usersList.map(function (user) {
return <li>{user.name}</li>
})
// es 6
const users = usersList.map(user => <li>{user.name}</li>);
The Concurrent rendering makes React apps to be more responsive by rendering component trees without blocking the main UI thread. It allows React to interrupt a long-running render to handle a high-priority event. i.e, When you enabled concurrent Mode, React will keep an eye on other tasks that need to be done, and if there's something with a higher priority it will pause what it is currently rendering and let the other task finish first. You can enable this in two ways,
// 1. Part of an app by wrapping with ConcurrentMode
<React.unstable_ConcurrentMode>
<Something />
</React.unstable_ConcurrentMode>
// 2. Whole app using createRoot
ReactDOM.unstable_createRoot(domNode).render(<App />);
Yes, you can use javascript: URLs but it will log a warning in the console. Because URLs starting with javascript: are dangerous by including unsanitized output in a tag like and create a security hole.
<a href>
const companyProfile = {
website: "javascript: alert('Your website is hacked')",
};
// It will log a warning
<a href={companyProfile.website}>More details</a>
Remember that the future versions will throw an error for javascript URLs.
The ESLint plugin enforces rules of Hooks to avoid bugs. It assumes that any function starting with ”use” and a capital letter right after it is a Hook. In particular, the rule enforces that,
Imagine a simple UI component, such as a "Like" button. When you tap it, it turns blue if it was previously grey, and grey if it was previously blue.
The imperative way of doing this would be:
if( user.likes() ) {
if( hasBlue() ) {
removeBlue();
addGrey();
} else {
removeGrey();
addBlue();
}
}
Basically, you have to check what is currently on the screen and handle all the changes necessary to redraw it with the current state, including undoing the changes from the previous state. You can imagine how complex this could be in a real-world scenario.
In contrast, the declarative approach would be:
if( this.state.liked ) {
return <blueLike />;
} else {
return <greyLike />;
}
Because the declarative approach separates concerns, this part of it only needs to handle how the UI should look in a sepecific state, and is therefore much simpler to understand.
Below are some of the benefits of using typescript with Reactjs,
When a user logs in and reload, to persist the state generally we add the load user action in the useEffect hooks in the main App.js. While using Redux, loadUser action can be easily accessed.
App.js
import {loadUser} from '../actions/auth';
store.dispatch(loadUser());
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import AuthState from './context/auth/AuthState'
ReactDOM.render(
<React.StrictMode>
<AuthState>
<App />
</AuthState>
</React.StrictMode>,
document.getElementById('root')
);
App.js
const authContext = useContext(AuthContext);
const { loadUser } = authContext;
useEffect(() => {
loadUser();
},[])
loadUser
const loadUser = async () => {
const token = sessionStorage.getItem('token');
if(!token){
dispatch({
type: ERROR
})
}
setAuthToken(token);
try {
const res = await axios('/api/auth');
dispatch({
type: USER_LOADED,
payload: res.data.data
})
} catch (err) {
console.error(err);
}
}
There are three major benefits of new JSX transform,
The new JSX transform doesn’t require React to be in scope. i.e, You don't need to import React package for simple scenarios.
Let's take an example to look at the main differences between the old and the new transform,
Old Transform:
import React from 'react';
function App() {
return <h1>Good morning!!</h1>;
}
Now JSX transform convert the above code into regular JavaScript as below,
import React from 'react';
function App() {
return React.createElement('h1', null, 'Good morning!!');
}
New Transform:
The new JSX transform doesn't require any React imports
function App() {
return <h1>Good morning!!</h1>;
}
Under the hood JSX transform compiles to below code
import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1', { children: 'Good morning!!' });
}
Note: You still need to import React to use Hooks.
Redux team has provided official redux+js or redux+typescript templates for create-react-app project. The generated project setup includes,
<Provider>
The below commands need to be executed along with template option as below,
npx create-react-app my-app --template redux
npx create-react-app my-app --template redux-typescript
React Server Component is a way to write React component that gets rendered in the server-side with the purpose of improving React app performance. These components allow us to load components from the backend.
Note: React Server Components is still under development and not recommended for production yet.
State mutationhappens when you try to update the state of a component without actually using function. This can happen when you are trying to do some computations using a state variable and unknowingly save the result in the same state variable. This is the main reason why it is advised to return new instances of state variables from the reducers by using Object.assign({}, ...) or spread syntax.
setState
This can cause unknown issues in the UI as the value of the state variable got updated without telling React to check what all components were being affected from this update and it can cause UI bugs.
Ex:
class A extends React.component {
constructor(props) {
super(props);
this.state = {
loading: false
}
}
componentDidMount() {
let { loading } = this.state;
loading = (() => true)(); // Trying to perform an operation and directly saving in a state variable
}
How to prevent it: Make sure your state variables are immutable by either enforcing immutability by using plugins like Immutable.js, always using to make updates, and returning new instances in reducers when sending updated state values.
setState
class Example extends Reacts.Component { render(){ return <h1>This is a class component</h1>} }
I guess we all know that Pascal Case is the accepted way of naming a component in react.
Example
Functional component has been improved over the years with some added features like Hooks Here is a syntax for functional component
function App(){ return <div className="App"> <h1>Hello, I'm a Nigerian</h1> </div> }
states holds information or data about a component in react which may change over time. in class component we can update the state, when a user interacts with it or maybe a server response using the method and the initial state is been assigned in the method using the the object. different data type can be passed in the this.state object, which can be string, boolean, numbers, etc. A simple example showing how we use the setState() and constructor()
setState()
Constructor( )
this.state
class Example extends Component { constructor() { super(); this.state = { example: "This is a class component", }; } changeText() { this.setState({ example: "implementing the setState() in class component", }); } render() { return ( <> <h1>{this.state.example}</h1> <button onClick={() => { this.changeText(); }}> Click!! </button> </> ); } }
Initially, we could not use state in functional components because it was only supported in class components. over the years hooks were implemented in functional component. The hooks that enable us to use state in functional component is called useState The useState( ) hook returns an array of the current state and a function ( setState) that we can use to update the value of the current state. The array is being destructured so we can can see the variables the array holds that is, the initial state and the updated state lets see an example
function App() { const [country, setCountry] = useState("i'm a Nigerian"); const change = () => { setCountry("i am a Canadian"); }; return ( <div className="App"> <h1>Hello, {country} </h1> <button onClick={change}>Change</button> </div> ); }
class App extensions React.Component { render() { return (
## Props in Functional Components Props in functional components are similar to that of the class components, the difference is the absence of the 'this' keyword. We also destructure in a similar way without the 'this' keyword. they are also immutable in the functional component
函数 食物(道具) { 返回
函数 App() { return (
> As you can see in the example above there wasn't a need for the 'this' keyword.
函数 食物(道具) { const { fav } = props; return
函数 App() { return (
summary: *The functional components doesn't require the render method *Class component require the render() method that returns JSX. To use states in functional component we use the **useState** hook. To use State in class components we use the constructor method and the setState function. Functional components use the useEffect hook instead of lifecycle method [useEffect](https://www.w3schools.com/react/react_useeffect.asp ) Class components uses Lifecycle method **componentWillUnmount** etc. [ Lifecycle methods](https://www.w3schools.com/react/react_lifecycle.asp ) **[⬆ Back to Top](#table-of-contents)** ## Disclaimer The questions provided in this repository are the summary of frequently asked questions across numerous companies. We cannot guarantee that these questions will actually be asked during your interview process, nor should you focus on memorizing all of them. The primary purpose is for you to get a sense of what some companies might ask — do not get discouraged if you don't know the answer to all of them — that is ok! Good luck with your interview 😊 ---