React 開發必須知道的 34 個技巧【近1W字】


React 開發必須知道的 34 個技巧【近1W字】


源碼地址

https://github.com/lanzhsh/react-vue-koa/tree/master/react-pc-skill

歡迎 star

效果圖

React 開發必須知道的 34 個技巧【近1W字】


1 組件通訊

1.1 props

子組件

<code>import React from "react";
import PropTypes from "prop-types";
import { Button } from "antd";

export default class EightteenChildOne extends React.Component {
static propTypes = { //propTypes校驗傳入類型,詳情在技巧11
name: PropTypes.string
};

click = () => {
// 通過觸發方法子傳父
this.props.eightteenChildOneToFather("這是 props 改變父元素的值");
};

render() {
return (

這是通過 props 傳入的值{this.props.name}

<button>
點擊改變父元素值
/<button>

);
}
}

複製代碼/<code>

父組件

<code><eightteenchildone>this.eightteenChildOneToFather(mode)}>/<eightteenchildone> 

// 或者
<eightteenchildone>
複製代碼/<code>

props 傳多個值時: 傳統寫法

<code>const {dataOne,dataTwo,dataThree} = this.state

複製代碼
/<code>

升級寫法

<code>
複製代碼
/<code>

1.2 props 升級版

原理:子組件裡面利用 props 獲取父組件方法直接調用,從而改變父組件的值 注意: 此方法和 props 大同小異,都是 props 的應用,所以在源碼中沒有舉例

調用父組件方法改變該值

<code>// 父組件
state = {
count: {}
}
changeParentState = obj => {
this.setState(obj);
}
// 子組件
onClick = () => {
this.props.changeParentState({ count: 2 });
}
複製代碼/<code>

1.3 Provider,Consumer和Context

1.Context在 16.x 之前是定義一個全局的對象,類似 vue 的 eventBus,如果組件要使用到該值直接通過this.context獲取

<code>//根組件
class MessageList extends React.Component {
getChildContext() {
return {color: "purple",text: "item text"};
}

render() {
const {messages} = this.props || {}
const children = messages && messages.map((message) =>
<message>
);
return
{children}
;
}
}

MessageList.childContextTypes = {
color: React.PropTypes.string
text: React.PropTypes.string
};

//中間組件
class Message extends React.Component {
render() {
return (

<messageitem>
<button>Delete/<button>

);
}
}

//孫組件(接收組件)
class MessageItem extends React.Component {
render() {
return (

{this.context.text}

);
}
}

MessageItem.contextTypes = {
text: React.PropTypes.string //React.PropTypes在 15.5 版本被廢棄,看項目實際的 React 版本
};

class Button extends React.Component {
render() {
return (
<button>
{this.props.children}
/<button>
);
}
}

Button.contextTypes = {
color: React.PropTypes.string
};
複製代碼/<code>

2.16.x 之後的Context使用了Provider和Customer模式,在頂層的Provider中傳入value,在子孫級的Consumer中獲取該值,並且能夠傳遞函數,用來修改context 聲明一個全局的 context 定義,context.js

<code>import React from 'react'
let { Consumer, Provider } = React.createContext();//創建 context 並暴露Consumer和Provider模式
export {
Consumer,
Provider
}
複製代碼/<code>

父組件導入

<code>// 導入 Provider
import {Provider} from "../../utils/context"

<provider>


父組件定義的值:{name}


<eightteenchildtwo>

/<provider>
複製代碼/<code>

子組件

<code>// 導入Consumer
import { Consumer } from "../../utils/context"
function Son(props) {
return (
//Consumer容器,可以拿到上文傳遞下來的name屬性,並可以展示對應的值
<consumer>
{name => (
style={{
border: "1px solid blue",
width: "60%",
margin: "20px auto",
textAlign: "center"
}}
>
// 在 Consumer 中可以直接通過 name 獲取父組件的值

子組件。獲取父組件的值:{name}



)}
/<consumer>
);
}
export default Son;
複製代碼/<code>

1.4 EventEmitter

EventEmiter 傳送門 使用 events 插件定義一個全局的事件機制

1.5 路由傳參

1.params

<code><route>
<link>xxx
this.props.history.push({pathname:"/path/" + name});
讀取參數用:this.props.match.params.name
複製代碼/<route>/<code>

2.query

<code><route>
<link>
this.props.history.push({pathname:"/query",query: { name : 'sunny' }});
讀取參數用: this.props.location.query.name
複製代碼/<route>/<code>

3.state

<code><route>
<link>
this.props.history.push({pathname:"/sort ",state : { name : 'sunny' }});
讀取參數用: this.props.location.query.state
複製代碼/<route>/<code>

4.search

<code><route>
<link>xxx
this.props.history.push({pathname:`/web/search?id ${row.id}`});
讀取參數用: this.props.location.search
複製代碼/<route>/<code>

這個在 react-router-dom: v4.2.2有 bug,傳參跳轉頁面會空白,刷新才會加載出來

5.優缺點

<code>1.params在HashRouter和BrowserRouter路由中刷新頁面參數都不會丟失 

2.state在BrowserRouter中刷新頁面參數不會丟失,在HashRouter路由中刷新頁面會丟失
3.query:在HashRouter和BrowserRouter路由中刷新頁面參數都會丟失
4.query和 state 可以傳對象
複製代碼/<code>

1.6 onRef

原理:onRef 通訊原理就是通過 props 的事件機制將組件的 this(組件實例)當做參數傳到父組件,父組件就可以操作子組件的 state 和方法

EightteenChildFour.jsx

<code>export default class EightteenChildFour extends React.Component {
state={
name:'這是組件EightteenChildFour的name 值'
}

componentDidMount(){
this.props.onRef(this)
console.log(this) // ->將EightteenChildFour傳遞給父組件this.props.onRef()方法
}

click = () => {
this.setState({name:'這是組件click 方法改變EightteenChildFour改變的name 值'})
};

render() {
return (

{this.state.name}

<button>
點擊改變組件EightteenChildFour的name 值
/<button>

);
}
}
複製代碼/<code>

eighteen.jsx

<code><eightteenchildfour>

eightteenChildFourRef = (ref)=>{
console.log('eightteenChildFour的Ref值為')
// 獲取的 ref 裡面包括整個組件實例
console.log(ref)
// 調用子組件方法
ref.click()
}
複製代碼/<code>

1.7 ref

原理:就是通過 React 的 ref 屬性獲取到整個子組件實例,再進行操作

EightteenChildFive.jsx

<code>// 常用的組件定義方法
export default class EightteenChildFive extends React.Component {
state={
name:'這是組件EightteenChildFive的name 值'
}

click = () => {
this.setState({name:'這是組件click 方法改變EightteenChildFive改變的name 值'})
};

render() {
return (

{this.state.name}

<button>

點擊改變組件EightteenChildFive的name 值
/<button>

);
}
}
複製代碼/<code>

eighteen.jsx

<code>// 鉤子獲取實例
componentDidMount(){
console.log('eightteenChildFive的Ref值為')
// 獲取的 ref 裡面包括整個組件實例,同樣可以拿到子組件的實例
console.log(this.refs["eightteenChildFiveRef"])
}

// 組件定義 ref 屬性
<eightteenchildfive>
複製代碼/<code>

1.8 redux

redux 是一個獨立的事件通訊插件,這裡就不做過多的敘述 請戳傳送門:

1.9 MobX

MobX 也是一個獨立的事件通訊插件,這裡就不做過多的敘述 請戳傳送門:

1.10 flux

flux 也是一個獨立的事件通訊插件,這裡就不做過多的敘述 請戳傳送門:

1.11 hooks

1.hooks 是利用 userReducer 和 context 實現通訊,下面模擬實現一個簡單的 redux 2.核心文件分為 action,reducer,types action.js

<code>import * as Types from './types';

export const onChangeCount = count => ({
type: Types.EXAMPLE_TEST,
count: count + 1
})
複製代碼/<code>

reducer.js

<code>import * as Types from "./types";
export const defaultState = {
count: 0
};
export default (state, action) => {
switch (action.type) {
case Types.EXAMPLE_TEST:
return {
...state,
count: action.count
};
default: {
return state;
}
}
};
複製代碼/<code>

types.js

<code>export const EXAMPLE_TEST = 'EXAMPLE_TEST';
複製代碼/<code>

eightteen.jsx

<code>export const ExampleContext = React.createContext(null);//創建createContext上下文

// 定義組件
function ReducerCom() {
const [exampleState, exampleDispatch] = useReducer(example, defaultState);

return (
<examplecontext.provider> value={{ exampleState, dispatch: exampleDispatch }}
>
<eightteenchildthree>
/<examplecontext.provider>
);

}
複製代碼/<code>

EightteenChildThree.jsx // 組件

<code>import React, {  useEffect, useContext } from 'react';
import {Button} from 'antd'

import {onChangeCount} from '../../pages/TwoTen/store/action';
import { ExampleContext } from '../../pages/TwoTen/eighteen';

const Example = () => {

const exampleContext = useContext(ExampleContext);

useEffect(() => { // 監聽變化
console.log('變化執行啦')
}, [exampleContext.exampleState.count]);

return (

值為{exampleContext.exampleState.count}


<button> exampleContext.dispatch(onChangeCount(exampleContext.exampleState.count))}>點擊加 1/<button>

)
}

export default Example;
複製代碼/<code>

3.hooks其實就是對原有React 的 API 進行了封裝,暴露比較方便使用的鉤子;

4.鉤子有:

鉤子名 作用 useState 初始化和設置狀態 useEffect componentDidMount,componentDidUpdate和componentWillUnmount和結合體,所以可以監聽useState定義值的變化 useContext 定義一個全局的對象,類似 context useReducer 可以增強函數提供類似 Redux 的功能 useCallback 記憶作用,共有兩個參數,第一個參數為一個匿名函數,就是我們想要創建的函數體。第二參數為一個數組,裡面的每一項是用來判斷是否需要重新創建函數體的變量,如果傳入的變量值保持不變,返回記憶結果。如果任何一項改變,則返回新的結果 useMemo 作用和傳入參數與 useCallback 一致,useCallback返回函數,useDemo 返回值 useRef 獲取 ref 屬性對應的 dom useImperativeMethods 自定義使用ref時公開給父組件的實例值 useMutationEffect 作用與useEffect相同,但在更新兄弟組件之前,它在React執行其DOM改變的同一階段同步觸發 useLayoutEffect 作用與useEffect相同,但在所有DOM改變後同步觸發

5.useImperativeMethods

<code>function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeMethods(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return ;
}
FancyInput = forwardRef(FancyInput);
複製代碼/<code>

1.12 slot

slot 就是將父組件的標籤傳給子組件,類似vue 的 v-slot 場景:有些組件只是共用部分dom 邏輯,裡面有部分邏輯是獨立的

<code>// 父組件文件
import SlotChild from 'SlotChild'

<slotchild>slot={
這是父組件的 slot
}>
/<slotchild>

// 子組件
子組件直接獲取 this.props.slot 就可獲取到內容
複製代碼/<code>

1.13 對比

方法 優點 缺點 props 不需要引入外部插件 兄弟組件通訊需要建立共同父級組件,麻煩 props 升級版 不需要引入外部插件,子傳父,不需要在父組件用方法接收 同 props Provider,Consumer和Context 不需要引入外部插件,跨多級組件或者兄弟組件通訊利器 狀態數據狀態追蹤麻煩 EventEmitter 可支持兄弟,父子組件通訊 要引入外部插件 路由傳參 可支持兄弟組件傳值,頁面簡單數據傳遞非常方便 父子組件通訊無能為力 onRef 可以在獲取整個子組件實例,使用簡單 兄弟組件通訊麻煩,官方不建議使用 ref 同 onRef 同 onRef redux 建立了全局的狀態管理器,兄弟父子通訊都可解決 引入了外部插件 mobx 建立了全局的狀態管理器,兄弟父子通訊都可解決 引入了外部插件 flux 建立了全局的狀態管理器,兄弟父子通訊都可解決 引入了外部插件 hooks 16.x 新的屬性,可支持兄弟,父子組件通訊 需要結合 context 一起使用 slot 支持父向子傳標籤

redux , mobx和flux對比

方法 介紹 redux 1.核心模塊:Action,Reducer,Store;2. Store 和更改邏輯是分開的;3. 只有一個 Store;4. 帶有分層 reducer 的單一 Store;5. 沒有調度器的概念;6. 容器組件是有聯繫的;7. 狀態是不可改變的;8.更多的是遵循函數式編程思想 mobx 1.核心模塊:Action,Reducer,Derivation;2.有多個 store;3.設計更多偏向於面向對象編程和響應式編程,通常將狀態包裝成可觀察對象,一旦狀態對象變更,就能自動獲得更新 flux 1.核心模塊:Store,ReduceStore,Container;2.有多個 store;

2.require.context()

這個是 webpack 的 api,這個在 vue 技巧中有介紹,因為 Vue 和 React 工程都是基於 webpack打包,所以在 react 也可以使用

<code>const path = require('path')
const files = require.context('@/components/home', false, /\\.vue$/)
const modules = {}
files.keys().forEach(key => {
const name = path.basename(key, '.vue')
modules[name] = files(key).default || files(key)
})

複製代碼/<code>

3.Decorator

定義:decorator是ES7的一個新特性,可以修改class的屬性

<code>import React from 'react'
import Test from '../../utils/decorators'

@Test
//只要Decorator後面是Class,默認就已經把Class當成參數隱形傳進Decorator了。
class TwentyNine extends React.Component{
componentDidMount(){

console.log(this,'decorator.js') // 這裡的this是類的一個實例
console.log(this.testable)
}
render(){
return (
這是技巧23

)
}
}

export default TwentyNine
複製代碼/<code>

decorators.js

<code>function testable(target) {
console.log(target)
target.isTestable = true;
target.prototype.getDate = ()=>{
console.log( new Date() )
}
}

export default testable
複製代碼/<code>

很多中間件,像 redux 裡面就封裝了Decorator的使用

4.使用 if...else

場景:有些時候需要根據不同狀態值頁面顯示不同內容

<code>import React from "react";

export default class Four extends React.Component {
state = {
count: 1
};
render() {
let info
if(this.state.count===0){
info=(

這是數量為 0 顯示
)
} else if(this.state.count===1){
info=(
這是數量為 1 顯示
)
}
return (

{info}

);
}
}
複製代碼/<code>

5.state 值改變的五種方式

方式 1

<code>let {count} = this.state
this.setState({count:2})
複製代碼/<code>

方式 2:callBack

<code>this.setState(({count})=>({count:count+2}))
複製代碼/<code>

方式 3:接收 state 和 props 參數

<code>this.setState((state, props) => {
return { count: state.count + props.step };
});
複製代碼/<code>

方式 4:hooks

<code>const [count, setCount] = useState(0)
// 設置值
setCount(count+2)
複製代碼/<code>

方式 5:state 值改變後調用

<code>this.setState(
{count:3},()=>{
//得到結果做某種事
}
)
複製代碼/<code>

6.監聽states 變化

1.16.x 之前使用componentWillReceiveProps

<code>componentWillReceiveProps (nextProps){
if(this.props.visible !== nextProps.visible){
//props 值改變做的事
}
}
複製代碼/<code>

注意:有些時候componentWillReceiveProps在 props 值未變化也會觸發,因為在生命週期的第一次render後不會被調用,但是會在之後的每次render中被調用 = 當父組件再次傳送props

2.16.x 之後使用getDerivedStateFromProps,16.x 以後componentWillReveiveProps也未移除

<code>export default class Six extends React.Component {
state = {
countOne:1,
changeFlag:''
};
clickOne(){
let {countOne} = this.state
this.setState({countOne:countOne+1})
};
static getDerivedStateFromProps (nextProps){
console.log('變化執行')
return{
changeFlag:'state 值變化執行'
}
}

render() {
const {countOne,changeFlag} = this.state
return (


<button>點擊加 1/<button>countOne 值為{countOne}
{changeFlag}



);
}
}
複製代碼/<code>

7.組件定義方法

方式 1:ES5 的Function 定義

<code>function FunCom(props){
return
這是Function 定義的組件

}
ReactDOM.render(<funcom>, mountNode)

// 在 hooks 未出來之前,這個是定義無狀態組件的方法,現在有了 hooks 也可以處理狀態
複製代碼/<code>

方式 2: ES5的 createClass 定義

<code>const CreateClassCom = React.createClass({
render: function() {
return
這是React.createClass定義的組件

}
});

複製代碼/<code>

方式 3:ES6 的 extends

<code>class Com extends React.Component {
render(){
return(
這是React.Component定義的組件
)
}
}
複製代碼/<code>

調用

<code>export default class Seven extends React.Component {
render() {
return (

<funcom>


);
}
}
複製代碼/<code>

區別: ES5的 createClass是利用function模擬class的寫法做出來的es6; 通過es6新增class的屬性創建的組件此組件創建簡單.

8.通過 ref 屬性獲取 component

方式 1:也是最早的用法,通過 this.refs[屬性名獲取] 也可以作用到組件上,從而拿到組件實例

<code>class RefOne extends React.Component{
componentDidMount() {
this.refs['box'].innerHTML='這是 div 盒子,通過 ref 獲取'
}

render(){
return(

)
}
}
複製代碼/<code>

方式 2:回調函數,在dom節點或組件上掛載函數,函數的入參是dom節點或組件實例,達到的效果與字符串形式是一樣的,都是獲取其引用

<code>class RefTwo extends React.Component{
componentDidMount() {
this.input.value='這是輸入框默認值';
this.input.focus();
}
render(){
return(
{ this.input = comp; }}/>
)
}
}
複製代碼/<code>

方式 3:React.createRef() React 16.3版本後,使用此方法來創建ref。將其賦值給一個變量,通過ref掛載在dom節點或組件上,該ref的current屬性,將能拿到dom節點或組件的實例

<code>class RefThree extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
componentDidMount(){
console.log(this.myRef.current);
}
render(){
return
}
}

複製代碼/<code>

方式 4:React.forwardRef React 16.3版本後提供的,可以用來創建子組件,以傳遞ref

<code>class RefFour extends React.Component{
constructor(props){
super(props);
this.myFourRef=React.forwardRef();
}
componentDidMount(){
console.log(this.myFourRef.current);
}
render(){
return <child>
}
}
複製代碼/<child>/<code>

子組件通過React.forwardRef來創建,可以將ref傳遞到內部的節點或組件,進而實現跨層級的引用。forwardRef在高階組件中可以獲取到原始組件的實例.這個功能在技巧 18 會著重講

9.static 使用

場景:聲明靜態方法的關鍵字,靜態方法是指即使沒有組件實例也可以直接調用

<code>export default class Nine extends React.Component {
static update(data) {
console.log('靜態方法調用執行啦')
}
render() {
return (

這是 static 關鍵字技能

);

}
}

Nine.update('2')
複製代碼/<code>

注意: 1.ES6的class,我們定義一個組件的時候通常是定義了一個類,而static則是創建了一個屬於這個類的屬性或者方法 2.組件則是這個類的一個實例,component的props和state是屬於這個實例的,所以實例還未創建 3.所以static並不是react定義的,而加上static關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,所以也是無法訪問到 this 4.getDerivedStateFromProps也是通過靜態方法監聽值,詳情請見技巧 6

10.constructor和super

回顧: 1.談這兩個屬性之前,先回顧一下ES6 函數定義方法 2.每一個使用class方式定義的類默認都有一個constructor函數, 這個函數是構造函數的主函數, 該函數體內部的this指向生成的實例 3.super關鍵字用於訪問和調用一個對象的父對象上的函數

<code>export default class Ten extends React.Component {
constructor() { // class 的主函數
super() // React.Component.prototype.constructor.call(this),其實就是拿到父類的屬性和方法
this.state = {
arr:[]
}
}
render() {
return (

這是技巧 10


);
}
}
複製代碼/<code>

11.PropTypes

場景:檢測傳入子組件的數據類型 類型檢查PropTypes自React v15.5起已棄用,請使用prop-types 方式 1:舊的寫法

<code>class PropTypeOne extends React.Component {
render() {
return (

{this.props.email}

{this.props.name}


);
}
}

PropTypeOne.propTypes = {
name: PropTypes.string, //值可為array,bool,func,number,object,symbol
email: function(props, propName, componentName) { //自定義校驗
if (
!/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/.test(
props[propName]
)
) {
return new Error(
"組件" + componentName + "裡的屬性" + propName + "不符合郵箱的格式"
);
}
},
};
複製代碼/<code>

方法 2:利用 ES7 的靜態屬性關鍵字 static

<code>class PropTypeTwo extends React.Component {
static propTypes = {
name:PropTypes.string
};
render() {
return (

{this.props.name}


);
}
}
複製代碼/<code>

場景:可以在不使用構造函數的情況下初始化本地狀態,並通過使用箭頭函數聲明類方法,而無需額外對它們進行綁定

<code>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 (

{this.state.value}

<button>+/<button>

<button>-/<button>

)
}
}
複製代碼/<code>

13.異步組件

1.場景:路由切換,如果同步加載多個頁面路由會導致緩慢

2.核心 API: loader:需要加載的組件 loading:未加載出來的頁面展示組件 delay:延遲加載時間 timeout:超時時間

3.使用方法: 安裝 react-loadable ,babel插件安裝 syntax-dynamic-import. react-loadable是通過webpack的異步import實現的

<code>const Loading = () => {
return
loading
;
};

const LoadableComponent = Loadable({
loader: () => import("../../components/TwoTen/thirteen"),
loading: Loading
});

export default class Thirteen extends React.Component {
render() {
return <loadablecomponent>;
}
}
複製代碼/<code>

4.Loadable.Map() 並行加載多個資源的高階組件

14.動態組件

場景:做一個 tab 切換時就會涉及到組件動態加載 實質上是利用三元表達式判斷組件是否顯示

<code>class FourteenChildOne extends React.Component {
render() {
return
這是動態組件 1
;
}
}

class FourteenChildTwo extends React.Component {
render() {
return
這是動態組件 2
;
}
}

export default class Fourteen extends React.Component {
state={
oneShowFlag:true
}
tab=()=>{
this.setState({oneShowFlag:!this.state.oneShowFlag})
}
render() {
const {oneShowFlag} = this.state
return (

<button>顯示組件{oneShowFlag?2:1}/<button>
{oneShowFlag?<fourteenchildone>:<fourteenchildtwo>}
);
}
}
複製代碼/<code>

如果是單個組件是否顯示可以用短路運算

<code>oneShowFlag&&<fourteenchildone>
複製代碼/<code>

15.遞歸組件

場景:tree組件 利用React.Fragment或者 div 包裹循環

<code>class Item extends React.Component {
render() {
const list = this.props.children || [];
return (

{list.map((item, index) => {
return (
<react.fragment>

{item.name}


{// 當該節點還有children時,則遞歸調用本身
item.children && item.children.length ? (
<item>{item.children}/<item>
) : null}
/<react.fragment>
);
})}

);
}
}
複製代碼/<code>

16.受控組件和不受控組件

受控組件:組件的狀態通過React 的狀態值 state 或者 props 控制

<code>class Controll extends React.Component {
constructor() {
super();
this.state = { value: "這是受控組件默認值" };
}
render() {
return
{this.state.value}
;
}
}
複製代碼/<code>

不受控組件:組件不被 React的狀態值控制,通過 dom 的特性或者React 的ref 來控制

<code>class NoControll extends React.Component {
render() {
return
{this.props.value}
;
}
}
複製代碼/<code>

導入代碼:

<code>export default class Sixteen extends React.Component {
componentDidMount() {
console.log("ref 獲取的不受控組件值為", this.refs["noControll"]);
}
render() {
return (

<controll>
<nocontroll> value={"這是不受控組件傳入值"}
ref="noControll"
>/<nocontroll>

);
}
}
複製代碼/<code>

17.高階組件(HOC)

17.1 定義

1.就是類似高階函數的定義,將組件作為參數或者返回一個組件的組件; 2.作用: 抽取重複代碼,實現組件複用,常見場景,頁面複用; 條件渲染,控制組件的渲染邏輯(渲染劫持),常見場景,權限控制; 捕獲/劫持被處理組件的生命週期,常見場景,組件渲染性能追蹤、日誌打點

17.2 實現方法

1.屬性代理

<code>import React,{Component} from 'react';

const Seventeen = WrappedComponent =>
class extends React.Component {
render() {
const props = {
...this.props,
name: "這是高階組件"
};
return <wrappedcomponent>;
}
};

class WrappedComponent extends React.Component {
state={
baseName:'這是基礎組件'
}
render() {
const {baseName} = this.state
const {name} = this.props
return

基礎組件值為{baseName}

通過高階組件屬性代理的得到的值為{name}


}
}

export default Seventeen(WrappedComponent)
複製代碼/<code>

2.反向繼承 原理就是利用 super 改變改組件的 this 方向,繼而就可以在該組件處理容器組件的一些值

<code>  const Seventeen = (WrappedComponent)=>{
return class extends WrappedComponent {
componentDidMount() {
this.setState({baseName:'這是通過反向繼承修改後的基礎組件名稱'})
}
render(){
return super.render();
}
}
}

class WrappedComponent extends React.Component {
state={
baseName:'這是基礎組件'
}
render() {
const {baseName} = this.state
return

基礎組件值為{baseName}


}
}

export default Seventeen(WrappedComponent);
複製代碼/<code>

18.元素是否顯示

一般用三元表達式

<code> flag?
顯示內容
:''
flag&&
顯示內容

複製代碼/<code>

19.Dialog 組件創建

Dialog 應該是用的比較多的組件,下面有三種不同的創建方法 方式 1:通過 state 控制組件是否顯示

<code> class NineteenChildOne extends React.Component {
render() {
const Dialog = () =>
這是彈層1
;

return this.props.dialogOneFlag && <dialog>;
}
}
複製代碼/<code>

方式 2:通過ReactDom.render創建彈層-掛載根節點外層 通過原生的createElement,appendChild, removeChild和react 的ReactDOM.render,ReactDOM.unmountComponentAtNode來控制元素的顯示和隱藏

NineteenChild.jsx

<code>import ReactDOM from "react-dom";

class Dialog {
constructor(name) {
this.div = document.createElement("div");
this.div.style.width = "200px";
this.div.style.height = "200px";
this.div.style.backgroundColor = "green";
this.div.style.position = "absolute";
this.div.style.top = "200px";
this.div.style.left = "400px";
this.div.id = "dialog-box";
}
show(children) {
// 銷燬
const dom = document.querySelector("#dialog-box");
if(!dom){ //兼容多次點擊
// 顯示
document.body.appendChild(this.div);
ReactDOM.render(children, this.div);
}
}
destroy() {
// 銷燬

const dom = document.querySelector("#dialog-box");
if(dom){//兼容多次點擊
ReactDOM.unmountComponentAtNode(this.div);
dom.parentNode.removeChild(dom);
}
}
}
export default {
show: function(children) {
new Dialog().show(children);
},
hide: function() {
new Dialog().destroy();
}
};
複製代碼/<code>

nineteen.jsx

<code>twoSubmit=()=>{
Dialog.show('這是彈層2')
}

twoCancel=()=>{
Dialog.hide()
}
複製代碼/<code>

20.React.memo

作用:當類組件的輸入屬性相同時,可以使用 pureComponent 或 shouldComponentUpdate 來避免組件的渲染。現在,你可以通過把函數組件包裝在 React.memo 中來實現相同的功能

<code>import React from "react";

function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 傳入 render 方法的返回結果與
將 prevProps 傳入 render 方法的返回結果一致則返回 true,
否則返回 false
*/

if (prevProps.val === nextProps.val) {
return true;
} else {
return false;
}
}

// React.memo()兩個參數,第一個是純函數,第二個是比較函數
export default React.memo(function twentyChild(props) {
console.log("MemoSon rendered : " + Date.now());
return
{props.val}
;
}, areEqual);
複製代碼/<code>

21.React.PureComponent

作用: 1.React.PureComponent 和 React.Component類似,都是定義一個組件類。 2.不同是React.Component沒有實現shouldComponentUpdate(),而 React.PureComponent通過props和state的淺比較實現了。 3.React.PureComponent是作用在類中,而React.memo是作用在函數中。 4.如果組件的props和state相同時,render的內容也一致,那麼就可以使用React.PureComponent了,這樣可以提高組件的性能

<code>class TwentyOneChild extends React.PureComponent{  //組件直接繼承React.PureComponent
render() {
return
{this.props.name}

}
}

export default class TwentyOne extends React.Component{
render(){
return (

<twentyonechild>

)
}
}

複製代碼/<code>

22.React.Component

作用:是基於ES6 class的React組件,React允許定義一個class或者function作為組件,那麼定義一個組件類,就需要繼承React.Component

<code>export default class TwentyTwo extends React.Component{ //組件定義方法
render(){
return (
這是技巧22

)
}
}
複製代碼/<code>

23.在 JSX 打印 falsy 值

定義: 1.falsy 值 (虛值) 是在 Boolean 上下文中認定為 false 的值; 2.值有 0,"",'',``,null,undefined,NaN

<code>export default class TwentyThree extends React.Component{
state={myVariable:null}
render(){
return (
{String(this.state.myVariable)}

)
}
}
複製代碼/<code>

虛值如果直接展示,會發生隱式轉換,為 false,所以頁面不顯示

24.ReactDOM.createPortal

作用:組件的render函數返回的元素會被掛載在它的父級組件上,createPortal 提供了一種將子節點渲染到存在於父組件以外的 DOM 節點的優秀的方案

<code>import React from "react";
import ReactDOM from "react-dom";
import {Button} from "antd"

const modalRoot = document.body;

class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement("div");
this.el.style.width = "200px";
this.el.style.height = "200px";
this.el.style.backgroundColor = "green";
this.el.style.position = "absolute";
this.el.style.top = "200px";
this.el.style.left = "400px";
}

componentDidMount() {
modalRoot.appendChild(this.el);
}

componentWillUnmount() {
modalRoot.removeChild(this.el);
}

render() {
return ReactDOM.createPortal(this.props.children, this.el);
}
}

function Child() {
return (

這個是通過ReactDOM.createPortal創建的內容

);
}

export default class TwentyFour extends React.Component {
constructor(props) {
super(props);
this.state = { clicks: 0 };
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(prevState => ({
clicks: prevState.clicks + 1
}));
}

render() {
return (

<button>點擊加1/<button>

點擊次數: {this.state.clicks}


<modal>
<child>
/<modal>

);
}
}

複製代碼/<code>

這樣元素就追加到指定的元素下面啦

25.在 React 使用innerHTML

場景:有些後臺返回是 html 格式字段,就需要用到 innerHTML 屬性

<code>export default class TwentyFive extends React.Component {
render() {
return (
這是渲染的 HTML 內容" }}>

);
}
}
複製代碼/<code>

26.React.createElement

語法: React.createElement( type, [props], [...children] )

源碼:

<code>export default class TwentySix extends React.Component {
render() {
return (

{React.createElement(
"div",
{ id: "one", className: "two" },
React.createElement("span", { id: "spanOne" }, "這是第一個 span 標籤"),
React.createElement("br"),
React.createElement("span", { id: "spanTwo" }, "這是第二個 span 標籤")
)}

);
}
}
複製代碼/<code>

原理:實質上 JSX 的 dom 最後轉化為 js 都是React.createElement

<code>// jsx 語法

this is spanOne
this is spanTwo


// 轉化為 js
React.createElement(
"div",
{ id: "one", class: "two" },
React.createElement( "span", { id: "spanOne" }, "this is spanOne"),
React.createElement("span", { id: "spanTwo" }, "this is spanTwo")
);
複製代碼/<code>

27.React.cloneElement

語法:

<code>React.cloneElement(
element,
[props],
[...children]
)
複製代碼/<code>

作用:這個方法的作用是複製組件,給組件傳值或者添加屬性 核心代碼

<code>React.Children.map(children, child => {
return React.cloneElement(child, {
count: _this.state.count
});
});
複製代碼/<code>

28.React.Fragment

作用:React.Fragment可以讓你聚合一個子元素列表,並且不在DOM中增加額外節點 核心代碼

<code>render() {
const { info } = this.state;
return (

{info.map((item, index) => {
return (
<react.fragment>
{item.name}

{item.age}

/<react.fragment>
);
})}

);
}
複製代碼/<code>

29.循環元素

內部沒有封裝像 vue 裡面 v-for 的指令,而是通過 map 遍歷

<code>{arr.map((item,index)=>{
return(

{item.name}
{item.age}

)
})}
複製代碼/<code>

30.給 DOM 設置和獲取自定義屬性

作用:有些要通過自定義屬性傳值

<code>export default class Thirty extends React.Component {
click = e => {
console.log(e.target.getAttribute("data-row"));
};

render() {
return (


點擊獲取屬性


);
}
}
複製代碼/<code>

31.綁定事件

場景:交互就會涉及到事件點擊,然後點擊選中值傳參也是一個很常見場景

<code>import React from "react";
import { Button } from 'antd'

export default class Three extends React.Component {
state = {
flag: true,
flagOne: 1
};
click(data1,data2){
console.log('data1 值為',data1)
console.log('data2 值為',data2)
}
render() {
return (

<button>點擊事件/<button>

);
}
}

複製代碼/<code>

使用方法在源碼 routes.js 有詳細使用

32.React-Router

32.1 V3和 V4的區別

1.V3或者說V早期版本是把router 和 layout components 分開; 2.V4是集中式 router,通過 Route 嵌套,實現 Layout 和 page 嵌套,Layout 和 page 組件 是作為 router 的一部分; 3.在V3 中的 routing 規則是 exclusive,意思就是最終只獲取一個 route; 4.V4 中的 routes 默認是 inclusive 的,這就意味著多個; 可以同時匹配和呈現.如果只想匹配一個路由,可以使用Switch,在 中只有一個 會被渲染,同時可以再在每個路由添加exact,做到精準匹配 Redirect,瀏覽器重定向,當多有都不匹配的時候,進行匹配

32.2 使用

<code>import { HashRouter as Router, Switch  } from "react-router-dom";

class App extends React.Component{
render(){
const authPath = '/login' // 默認未登錄的時候返回的頁面,可以自行設置
let authed = this.props.state.authed || localStorage.getItem('authed') // 如果登陸之後可以利用redux修改該值
return (
<router>
<switch>
{renderRoutes(routes, authed, authPath)}
/<switch>
/<router>
)
}
}
複製代碼/<code>

V4是通過 Route 嵌套,實現 Layout 和 page 嵌套,Switch切換路由的作用

33.樣式引入方法

方式 1:import 導入

<code>import './App.css';
複製代碼/<code>

方式 2:內聯方式

<code>import React from 'react';

const Header = () => {

const heading = '頭部組件'

return(

{heading}



)
}

或者
import React from 'react';

const footerStyle = {
width: '100%',
backgroundColor: 'green',
padding: '50px',
font: '30px',
color: 'white',
fontWeight: 'bold'
}

export const Footer = () => {
return(

底部組件

)
}
複製代碼/<code>

34.動態綁定 className

原理:通過三元表達式控制 className 值

<code>render(){
const flag=true
return (
這是技巧 34

)
}
複製代碼/<code>

總結

這就是從實際項目開發總結的 React的 34 個技巧;

源碼地址:https://github.com/lanzhsh/react-vue-koa/tree/master/react-pc-skill

歡迎 star



分享到:


相關文章: