join us!
160eebcb1ecf7b " , to provide front-end developers with technical information and a series of basic articles. For a better user experience, please move to our official website rookies (160eebcb1ecf83 https://xhs-rookies.com/ ) to learn and get the latest articles in time.
"Code tailor" , if you are interested in our article or want to make some suggestions, follow Mountain" public account, contact us, you can also watch it on WeChat Our article. Every suggestion or approval is a great encouragement to us!
Preface
In this section, we will introduce the React
, what are the high-end components, and the supplements to the high-end components.
This article will introduce you to the following:
- Recognize high-end components
- Use of high-end components
- Significance of high-end components
- Points to note for high-end components
refs
high-end componentsPortals
Fragment
- Strict mode-
StrictMode
High-end components
Recognize high-end components
What are high-end components? I believe many students have heard of and used higher-order function , they are very similar, so we can first review what is higher-order function .
Wikipedia definition of higher-order function: at least one of the following conditions is met:
- Accept one or more functions as input;
- Output a function;
JavaScript
more common filter
, map
, reduce
are all higher-order functions.
So what are high-end components?
- Higher-order components in English is Higher-Order Components , abbreviated as
HOC
, is an advanced technique for multiplexing component logic inReact
- Official definition: parameter is the component and the return value is the new component;
From this, we can analyze:
- high-order component itself is not a component, but a function
- The parameter of this function is a component, and the return value is also a component
The calling process of high-level components is similar to this:
const EnhancedComponent = higherOrderComponent(WrappedComponent)
The component is to transform props into UI, and the higher-order component is to transform the component into another component.
The writing process of higher-order functions is similar to this:
- Return class components, suitable for stateful processing and life cycle requirements
function higherOrderComponent(WrapperComponent) {
return class NewComponent extends PureComponent {
render() {
return <WrapperComponent />
}
}
}
- Return function component, suitable for simple logic processing
function higherOrderComponent(WrapperComponent) {
return (props) => {
if (props.token) {
return <WrapperComponent />
} else {
return <></>
}
}
}
In ES6, the class name in the class expression can be omitted, so there is the following way of writing:
function higherOrderComponent(WrapperComponent) {
return class extends PureComponent {
render() {
return <WrapperComponent />
}
}
}
The component name can be modified displayName
function higherOrderComponent(WrapperComponent) {
class NewComponent extends PureComponent {
render() {
return <WrapperComponent />
}
}
NewComponent.displayName = 'xhsRookies'
return NewComponent
}
Note: high-end components are notReact API
, it is a design pattern formedReact
So, in our development, what can high-end components help us do? Look down!
Use of high-end components
enhancement of props
1. Without modifying the original code, add a new props attribute
Suppose we have the following case:
class XhsRookies extends PureComponent {
render() {
const { name, age } = this.props
return <h2>XhsRookies {name + age}</h2>
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<XhsRookies name="xhsRookies" age={18} />
</div>
)
}
}
We can pass a high-level components, without destroying the original props
the case of, for enhanced components, if need XhsRookies
components props
add a height
property, we can do this:
class XhsRookies extends PureComponent {
render() {
const { name, age } = this.props
return <h2>XhsRookies {name + age}</h2>
}
}
function enhanceProps(WrapperComponent, newProps) {
return (props) => <WrapperComponent {...props} {...newProps} />
}
const EnhanceHeader = enhanceProps(XhsRookies, { height: 1.88 })
export default class App extends PureComponent {
render() {
return (
<div>
<EnhanceHeader name="xhsRookies" age={18} />
</div>
)
}
}
Use high-level components to share Context
import React, { PureComponent, createContext } from 'react'
const UserContext = createContext({
nickname: '默认',
level: -1,
})
function XhsRookies(props) {
return (
<UserContext.Consumer>
{(value) => {
const { nickname, level } = value
return <h2>Header {'昵称:' + nickname + '等级' + level}</h2>
}}
</UserContext.Consumer>
)
}
export default class App extends PureComponent {
render() {
return (
<div>
<UserContext.Provider value={{ nickname: 'xhsRookies', level: 99 }}>
<XhsRookies />
</UserContext.Provider>
</div>
)
}
}
We define a high-level component ShareContextHOC
to share context
import React, { PureComponent, createContext } from 'react'
const UserContext = createContext({
nickname: '默认',
level: -1,
})
function ShareContextHOC(WrapperCpn) {
return (props) => {
return (
<UserContext.Consumer>
{(value) => {
return <WrapperCpn {...props} {...value} />
}}
</UserContext.Consumer>
)
}
}
function XhsRookies(props) {
const { nickname, level } = props
return <h2>Header {'昵称:' + nickname + '等级:' + level}</h2>
}
function Footer(props) {
const { nickname, level } = props
return <h2>Footer {'昵称:' + nickname + '等级:' + level}</h2>
}
const NewXhsRookies = ShareContextHOC(Header)
export default class App extends PureComponent {
render() {
return (
<div>
<UserContext.Provider value={{ nickname: 'xhsRookies', level: 99 }}>
<NewXhsRookies />
</UserContext.Provider>
</div>
)
}
}
Rendering judgment authentication
During development, we will encounter the following scenarios:
- Some pages must be successfully logged in to enter
- If the user does not log in successfully, jump directly to the login page
In this scenario, we can use high-level components to complete the authentication operation:
function LoginPage() {
// 登录页面
return <h2>LoginPage</h2>
}
function HomePage() {
// 登录成功可访问页面
return <h2>HomePage</h2>
}
export default class App extends PureComponent {
render() {
return (
<div>
<HomePage />
</div>
)
}
}
Use authentication components:
import React, { PureComponent } from 'react'
function loginAuthority(Page) {
return (props) => {
if (props.isLogin) {
// 如果登录成功 返回成功页面
return <Page />
} else {
// 如果为登录成功 返回登录页面
return <LoginPage />
}
}
}
function LoginPage() {
return <h2>LoginPage</h2>
}
function HomePage() {
return <h2>HomePage</h2>
}
const AuthorityPassPage = loginAuthority(HomePage)
export default class App extends PureComponent {
render() {
return (
<div>
<AuthorityPassPage isLogin={true} />
</div>
)
}
}
Life cycle hijacking
When multiple components need to do something in the life cycle, and these things are all the same logic, we can use high-level components to help these components in a unified way to complete these tasks, as shown in the following example:
import React, { PureComponent } from 'react'
class Home extends PureComponent {
componentDidMount() {
const nowTime = Date.now()
console.log(`Home渲染使用时间:${nowTime}`)
}
render() {
return (
<div>
<h2>Home</h2>
<p>我是home的元素,哈哈哈</p>
</div>
)
}
}
class Detail extends PureComponent {
componentDidMount() {
const nowTime = Date.now()
console.log(`Detail渲染使用时间:${nowTime}`)
}
render() {
return (
<div>
<h2>Detail</h2>
<p>我是detail的元素,哈哈哈</p>
</div>
)
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<Home />
<Detail />
</div>
)
}
}
We can use high-end home
prices to help complete the componentDidMount
life cycle function of the 060eebcb1ed5ee component and the detail
import React, { PureComponent } from 'react'
function logRenderTime(WrapperCpn) {
return class extends PureComponent {
componentDidMount() {
const nowTime = Date.now()
console.log(`${WrapperCpn.name}渲染使用时间:${nowTime}`)
}
render() {
return <WrapperCpn {...this.props} />
}
}
}
class Home extends PureComponent {
render() {
return (
<div>
<h2>Home</h2>
<p>我是home的元素,哈哈哈</p>
</div>
)
}
}
class Detail extends PureComponent {
render() {
return (
<div>
<h2>Detail</h2>
<p>我是detail的元素,哈哈哈</p>
</div>
)
}
}
const LogHome = logRenderTime(Home)
const LogDetail = logRenderTime(Detail)
export default class App extends PureComponent {
render() {
return (
<div>
<LogHome />
<LogDetail />
</div>
)
}
}
Significance of high-end components
Through the use of high-level components in the above different situations, we can find that using high-level components can perform more elegant processing React
In fact, the early React
provided a way to reuse components between mixin
, which is no longer recommended:
Mixin
may be mutually dependent and coupled, which is not conducive to code maintenance- The methods in different
Mixin
may conflict with each other Mixin
is very much, the component can be perceived, and even related processing is required for it, which will cause snowball complexity to the code
Of course, HOC also has some shortcomings of its own:
HOC
needs to be wrapped or nested on the original components. If a large number ofHOC
used, there will be a lot of nesting, which makes debugging very difficult;HOC
can hijackprops
, and it may cause conflicts if the agreement is not complied with;
Reasonable use of high-end components will be of great help to our development.
Points to note for high-end components
Don't use HOC in the render method
React
of diff
algorithm (called coordination) using the identifier to determine whether it is component should be updated prior subtree or discarded and mount a new subtree. If render
is the same as the component in the previous rendering ( ===
), then React
recursively updates the subtree by distinguishing the subtree from the new subtree. If they are not equal, the previous subtree is completely unloaded.
Normally, you don't need to think about this. But HOC
, because it means that you should not HOC
render
method of the component:
render() {
// 每次调用 render 函数都会创建一个新的 EnhancedComponent
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// 这将导致子树每次渲染都会进行卸载,和重新挂载的操作!
return <EnhancedComponent />;
}
This is not just a performance issue-remounting a component will cause the state of the component and all its subcomponents to be lost.
HOC
outside of the component, the component will only be created once. Therefore, it will be the same component render
Generally speaking, this is consistent with your expected performance.
const EnhancedComponent = enhance(MyComponent)
class App extends PureComponent {
render() {
return <EnhancedComponent />
}
}
In rare cases, you need to call HOC
dynamically. You can call it in the life cycle method of the component or its constructor.
refs will not be passed
Although the agreement of higher-order components is to pass all props
to the packaged component, this does not apply refs
That's because ref
is not actually a prop
, just like key
, it is specially processed React
If ref
added to HOC
return assembly, the ref
references to the container assembly, rather than packaging assembly.
Supplement of components
Forward refs in high-end components
Earlier we mentioned that in high-end components, refs
will not be passed, but we may encounter the need to forward refs
in high-end components during development, so how do we solve it? Fortunately, we can use the React.forwardRef
API to help solve this problem.
Let's start with an output component props
to the console HOC
start Example:
function logProps(WrappedComponent) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps)
console.log('new props:', this.props)
}
render() {
return <WrappedComponent {...this.props} />
}
}
return LogProps
}
logProps
HOC
penetrates all props
to its packaged components, so the rendering result will be the same. For example: we can use this HOC
record all passed to the " fancy button
" component props
:
class FancyButton extends React.Component {
focus() {
// ...
}
// ...
}
// 我们导出 LogProps,而不是 FancyButton。
// 虽然它也会渲染一个 FancyButton。
export default logProps(FancyButton)
refs
to now, this example is as mentioned earlier, 060eebcb1ed9a4 will not be passed through. If you HOC
add ref
, the ref
references outermost container component, not the component being wrapped.
import FancyButton from './FancyButton'
const ref = React.createRef()
// 我们导入的 FancyButton 组件是高阶组件(HOC)LogProps。
// 尽管渲染结果将是一样的,
// 但我们的 ref 将指向 LogProps 而不是内部的 FancyButton 组件!
// 这意味着我们不能调用例如 ref.current.focus() 这样的方法
;<FancyButton label="Click Me" handleClick={handleClick} ref={ref} />
At this time, we can use the React.forwardRef
API to explicitly refs
to the internal FancyButton
component. React.forwardRef
accepts a rendering function, which receives props
and ref
parameters and returns a React node.
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
// 将自定义的 prop 属性 “forwardedRef” 定义为 ref
return <Component ref={forwardedRef} {...rest} />
}
}
// 注意 React.forwardRef 回调的第二个参数 “ref”。
// 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
// 然后它就可以被挂载到被 LogProps 包裹的子组件上。
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />
})
}
In this way, we can pass refs
in high-level components.
Portals
In some cases, the content we want to render is independent of the parent component, or even independent of the currently mounted DOM
element (the default is mounted on the DOM
element with root
Portal provides an excellent solution for DOM
node that exists outside the parent component:
- The first parameter (
child
) is any renderableReact
child element, such as an element, string orfragment
; - The second parameter (
container
) is aDOM
element;
ReactDOM.createPortal(child, container)
Generally speaking, when you render
method of the component, the element will be mounted to the nearest parent node of the DOM
render() {
// React 挂载了一个新的 div,并且把子元素渲染其中
return (
<div>
{this.props.children}
</div>
);
}
However, sometimes it is beneficial to insert child elements into different positions in the DOM node:
render() {
// React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
// `domNode` 是一个可以在任何位置的有效 DOM 节点。
return ReactDOM.createPortal(
this.props.children,
domNode
);
}
For example, we are going to develop a TabBar
component, which can render its child components to the top position of the screen:
- The first step: modify
index.html
add a new node
<div id="root"></div>
<!-- 新节点 -->
<div id="TabBar"></div>
- Step 2: Write the style of this node
#TabBar {
position: fixed;
width: 100%;
height: 44px;
background-color: red;
}
- Step 3: Write component code
import React, { PureComponent } from 'react'
import ReactDOM from 'react-dom'
class TabBar extends PureComponent {
constructor(props) {
super(props)
}
render() {
return ReactDOM.createPortal(this.props.children, document.getElementById('TabBar'))
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<TabBar>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
</TabBar>
</div>
)
}
}
Fragment
In previous developments, we always wrapped a div element when returning content in a component:
export default class App extends PureComponent {
render() {
return (
<div>
<h2>微信公众号:小和山的菜鸟们</h2>
<button>点赞</button>
<button>关注</button>
</div>
)
}
}
Rendering result
We will find an div
element:
- This
div
element is needed for certain scenarios (for example, we want to put it in adiv
element, and then set the style specifically) - In some scenarios, this
div
is not necessary. For example, at present, I may want all the content to be directly rendered intoroot
;
When we delete this div
, an error will be reported. What should we do if we do not want to render this div
?
- Use
Fragment
Fragment
allows you to groupDOM
without adding additional nodes to 060eebcb1edea4;
export default class App extends PureComponent {
render() {
return (
<Fragment>
<h2>微信公众号:小和山的菜鸟们</h2>
<button>点赞</button>
<button>关注</button>
</Fragment>
)
}
}
The rendering effect is as follows:
React
also provides Fragment
It looks like empty label <></>
export default class App extends PureComponent {
render() {
return (
<>
<h2>微信公众号:小和山的菜鸟们</h2>
<button>点赞</button>
<button>关注</button>
</>
)
}
}
Note: if we needFragment
add attributes, such askey
, we can not use the grammar section
Strict Mode-StrictMode
StrictMode
is a tool used to highlight potential problems in the application. Like Fragment
, StrictMode
does not render any visible UI. It triggers additional checks and warnings for its descendant elements.
Note: strict mode checks only run in development mode; they will not affect production builds.
You can enable strict mode for any part of the application. E.g:
import React from 'react'
function ExampleApplication() {
return (
<div>
<Header />
<React.StrictMode>
<div>
<ComponentOne />
<ComponentTwo />
</div>
</React.StrictMode>
<Footer />
</div>
)
}
In the above example, will run strict mode checks on the Header
and Footer
However, ComponentOne
and ComponentTwo
and all their descendant elements will be checked.
StrictMode
currently helps:
- Identify unsafe life cycles
- Warning about using the obsolete
findDOMNode
- Detect unexpected side effects
- Detect outdated
context API
- Warning about using the obsolete string
ref API
1. Identify the unsafe life cycle
Certain outdated lifecycle methods are not safe to use React
However, if your application uses third-party libraries, it is difficult to ensure that they do not use these life cycle methods.
When strict mode is enabled, React
class
components that use the unsafe life cycle method, and print a warning message containing the information of these components, as shown below:
2. Warning about using outdated string ref API
Previously, React
provided two ways to manage refs
:
- Obsolete string
ref API
form - The form of the callback function
API
Despite a string ref API easier to use in both, but it has some disadvantages , therefore official recommended way callback .
React 16.3
adds a third option, which provides the ref
without any disadvantages:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.inputRef = React.createRef()
}
render() {
return <input type="text" ref={this.inputRef} />
}
componentDidMount() {
this.inputRef.current.focus()
}
}
Since the object ref
mainly added to replace the string ref
, strict mode now warns of the use of the string ref
.
3. Warning about using the obsolete findDOMNode method
React supports findDOMNode
to search for DOM nodes in the tree with a given class instance. Usually you don't need to do this, because you can bind ref directly to DOM nodes. Since this method is obsolete, I won't go into details here. If you are interested, you can learn by yourself.
4. Detect unexpected side effects
constructor
this component will be called twice;- This is a deliberate operation in strict mode, allowing you to see if some of the logic codes written here will be called multiple times, if there will be some side effects;
- In a production environment, it will not be called twice;
class Home extends PureComponent {
constructor(props) {
super(props)
console.log('home constructor')
}
UNSAFE_componentWillMount() {}
render() {
return <h2 ref="home">Home</h2>
}
}
5. Detect outdated context API
Early Context
by static
property declaration Context
object properties, by getChildContext
return Context
objects and other ways to use Context
of; but at the moment this method is outdated, obsolete context API
error-prone, will be removed in the next major version of. It is still valid in all 16.x
versions, but in strict mode, the following warning will be displayed:
Preview of the next section
In this section, we have studied the React
and the supplementary content of the components. In the next chapter, we will start a new study React-Router
, so stay tuned!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。