React 是前端三大框架之一,在開發中也是一項技能;
本文從實際開發中總結了 React 開發的一些技巧技巧,適合 React 初學或者有必定項目經驗的同窗
序列文章:Vue 開發必須知道的 36 個技巧【近1W字】css
請戳, 歡迎 starhtml
效果圖 前端
子組件vue
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 (
<div>
<div>這是經過 props 傳入的值{this.props.name}</div>
<Button type="primary" onClick={this.click}>
點擊改變父元素值
</Button>
</div>
);
}
}
複製代碼
父組件react
<EightteenChildOne name={'props 傳入的 name 值'} eightteenChildOneToFather={(mode)=>this.eightteenChildOneToFather(mode)}></EightteenChildOne>
複製代碼
props 傳多個值時:
傳統寫法webpack
const {dataOne,dataTwo,dataThree} = this.state
<Com dataOne={dataOne} dataTwo={dataTwo} dataThree={dataThree}>
複製代碼
升級寫法git
<Com {...{dataOne,dataTwo,dataThree}}>
複製代碼
原理:子組件裏面利用 props 獲取父組件方法直接調用,從而改變父組件的值
注意: 此方法和 props 大同小異,都是 props 的應用,因此在源碼中沒有舉例es6
調用父組件方法改變該值github
// 父組件
state = {
count: {}
}
changeParentState = obj => {
this.setState(obj);
}
// 子組件
onClick = () => {
this.props.changeParentState({ count: 2 });
}
複製代碼
1.Context在 16.x 以前是定義一個全局的對象,相似 vue 的 eventBus,若是組件要使用到該值直接經過this.context獲取web
//根組件
class MessageList extends React.Component {
getChildContext() {
return {color: "purple",text: "item text"};
}
render() {
const {messages} = this.props || {}
const children = messages && messages.map((message) =>
<Message text={message.text} />
);
return <div>{children}</div>;
}
}
MessageList.childContextTypes = {
color: React.PropTypes.string
text: React.PropTypes.string
};
//中間組件
class Message extends React.Component {
render() {
return (
<div>
<MessageItem />
<Button>Delete</Button>
</div>
);
}
}
//孫組件(接收組件)
class MessageItem extends React.Component {
render() {
return (
<div>
{this.context.text}
</div>
);
}
}
MessageItem.contextTypes = {
text: React.PropTypes.string //React.PropTypes在 15.5 版本被廢棄,看項目實際的 React 版本
};
class Button extends React.Component {
render() {
return (
<button style={{background: this.context.color}}>
{this.props.children}
</button>
);
}
}
Button.contextTypes = {
color: React.PropTypes.string
};
複製代碼
2.16.x 以後的Context使用了Provider和Customer模式,在頂層的Provider中傳入value,在子孫級的Consumer中獲取該值,而且可以傳遞函數,用來修改context 聲明一個全局的 context 定義,context.js
import React from 'react'
let { Consumer, Provider } = React.createContext();//建立 context 並暴露Consumer和Provider模式
export {
Consumer,
Provider
}
複製代碼
父組件導入
// 導入 Provider
import {Provider} from "../../utils/context"
<Provider value={name}>
<div style={{border:'1px solid red',width:'30%',margin:'50px auto',textAlign:'center'}}>
<p>父組件定義的值:{name}</p>
<EightteenChildTwo></EightteenChildTwo>
</div>
</Provider>
複製代碼
子組件
// 導入Consumer
import { Consumer } from "../../utils/context"
function Son(props) {
return (
//Consumer容器,能夠拿到上文傳遞下來的name屬性,並能夠展現對應的值
<Consumer>
{name => (
<div
style={{
border: "1px solid blue",
width: "60%",
margin: "20px auto",
textAlign: "center"
}}
>
// 在 Consumer 中能夠直接經過 name 獲取父組件的值
<p>子組件。獲取父組件的值:{name}</p>
</div>
)}
</Consumer>
);
}
export default Son;
複製代碼
EventEmiter 傳送門 使用 events 插件定義一個全局的事件機制
1.params
<Route path='/path/:name' component={Path}/>
<link to="/path/2">xxx</Link>
this.props.history.push({pathname:"/path/" + name});
讀取參數用:this.props.match.params.name
複製代碼
2.query
<Route path='/query' component={Query}/>
<Link to={{ path : '/query' , query : { name : 'sunny' }}}>
this.props.history.push({pathname:"/query",query: { name : 'sunny' }});
讀取參數用: this.props.location.query.name
複製代碼
3.state
<Route path='/sort ' component={Sort}/>
<Link to={{ path : '/sort ' , state : { name : 'sunny' }}}>
this.props.history.push({pathname:"/sort ",state : { name : 'sunny' }});
讀取參數用: this.props.location.query.state
複製代碼
4.search
<Route path='/web/search ' component={Search}/>
<link to="web/search?id=12121212">xxx</Link>
this.props.history.push({pathname:`/web/search?id ${row.id}`});
讀取參數用: this.props.location.search
複製代碼
5.優缺點
1.params和 search 只能傳字符串,刷新頁面參數不會丟
2.query和 state 能夠傳對象,可是刷新頁面參數會丟失
複製代碼
原理:onRef 通信原理就是經過 props 的事件機制將組件的 this(組件實例)當作參數傳到父組件,父組件就能夠操做子組件的 state 和方法
EightteenChildFour.jsx
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 (
<div>
<div>{this.state.name}</div>
<Button type="primary" onClick={this.click}>
點擊改變組件EightteenChildFour的name 值
</Button>
</div>
);
}
}
複製代碼
eighteen.jsx
<EightteenChildFour onRef={this.eightteenChildFourRef}></EightteenChildFour>
eightteenChildFourRef = (ref)=>{
console.log('eightteenChildFour的Ref值爲')
// 獲取的 ref 裏面包括整個組件實例
console.log(ref)
// 調用子組件方法
ref.click()
}
複製代碼
原理:就是經過 React 的 ref 屬性獲取到整個子組件實例,再進行操做
EightteenChildFive.jsx
// 經常使用的組件定義方法
export default class EightteenChildFive extends React.Component {
state={
name:'這是組件EightteenChildFive的name 值'
}
click = () => {
this.setState({name:'這是組件click 方法改變EightteenChildFive改變的name 值'})
};
render() {
return (
<div>
<div>{this.state.name}</div>
<Button type="primary" onClick={this.click}>
點擊改變組件EightteenChildFive的name 值
</Button>
</div>
);
}
}
複製代碼
eighteen.jsx
// 鉤子獲取實例
componentDidMount(){
console.log('eightteenChildFive的Ref值爲')
// 獲取的 ref 裏面包括整個組件實例,一樣能夠拿到子組件的實例
console.log(this.refs["eightteenChildFiveRef"])
}
// 組件定義 ref 屬性
<EightteenChildFive ref="eightteenChildFiveRef"></EightteenChildFive>
複製代碼
redux 是一個獨立的事件通信插件,這裏就不作過多的敘述 請戳傳送門:
MobX 也是一個獨立的事件通信插件,這裏就不作過多的敘述
請戳傳送門:
flux 也是一個獨立的事件通信插件,這裏就不作過多的敘述
請戳傳送門:
1.hooks 是利用 userReducer 和 context 實現通信,下面模擬實現一個簡單的 redux
2.核心文件分爲 action,reducer,types
action.js
import * as Types from './types';
export const onChangeCount = count => ({
type: Types.EXAMPLE_TEST,
count: count + 1
})
複製代碼
reducer.js
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;
}
}
};
複製代碼
types.js
export const EXAMPLE_TEST = 'EXAMPLE_TEST';
複製代碼
eightteen.jsx
export const ExampleContext = React.createContext(null);//建立createContext上下文
// 定義組件
function ReducerCom() {
const [exampleState, exampleDispatch] = useReducer(example, defaultState);
return (
<ExampleContext.Provider
value={{ exampleState, dispatch: exampleDispatch }}
>
<EightteenChildThree></EightteenChildThree>
</ExampleContext.Provider>
);
}
複製代碼
EightteenChildThree.jsx // 組件
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 (
<div>
<p>值爲{exampleContext.exampleState.count}</p>
<Button onClick={() => exampleContext.dispatch(onChangeCount(exampleContext.exampleState.count))}>點擊加 1</Button>
</div>
)
}
export default Example;
複製代碼
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
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeMethods(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
複製代碼
更多hooks 介紹請戳
方法 | 優勢 | 缺點 |
---|---|---|
props | 不須要引入外部插件 | 兄弟組件通信須要創建共同父級組件,麻煩 |
props 升級版 | 不須要引入外部插件,子傳父,不須要在父組件用方法接收 | 同 props |
Provider,Consumer和Context | 不須要引入外部插件,跨多級組件或者兄弟組件通信利器 | 狀態數據狀態追蹤麻煩 |
EventEmitter | 可支持兄弟,父子組件通信 | 要引入外部插件 |
路由傳參 | 可支持兄弟組件傳值,頁面簡單數據傳遞很是方便 | 父子組件通信無能爲力 |
onRef | 能夠在獲取整個子組件實例,使用簡單 | 兄弟組件通信麻煩,官方不建議使用 |
ref | 同 onRef | 同 onRef |
redux | 創建了全局的狀態管理器,兄弟父子通信均可解決 | 引入了外部插件 |
mobx | 創建了全局的狀態管理器,兄弟父子通信均可解決 | 引入了外部插件 |
flux | 創建了全局的狀態管理器,兄弟父子通信均可解決 | 引入了外部插件 |
hooks | 16.x 新的屬性,可支持兄弟,父子組件通信 | 須要結合 context 一塊兒使用 |
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; |
這個是 webpack 的 api,這個在 vue 技巧中有介紹,由於 Vue 和 React 工程都是基於 webpack打包,因此在 react 也可使用
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)
})
複製代碼
定義:decorator是ES7的一個新特性,能夠修改class的屬性
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 (
<div>這是技巧23</div>
)
}
}
export default TwentyNine
複製代碼
decorators.js
function testable(target) {
console.log(target)
target.isTestable = true;
target.prototype.getDate = ()=>{
console.log( new Date() )
}
}
export default testable
複製代碼
不少中間件,像 redux 裏面就封裝了Decorator的使用
場景:有些時候須要根據不一樣狀態值頁面顯示不一樣內容
import React from "react";
export default class Four extends React.Component {
state = {
count: 1
};
render() {
let info
if(this.state.count===0){
info=(
<span>這是數量爲 0 顯示</span>
)
} else if(this.state.count===1){
info=(
<span>這是數量爲 1 顯示</span>
)
}
return (
<div>
{info}
</div>
);
}
}
複製代碼
方式 1
let {count} = this.state
this.setState({count:2})
複製代碼
方式 2:callBack
this.setState(({count})=>({count:count+2}))
複製代碼
方式 3:接收 state 和 props 參數
this.setState((state, props) => {
return { count: state.count + props.step };
});
複製代碼
方式 4:hooks
const [count, setCount] = useState(0)
// 設置值
setCount(count+2)
複製代碼
方式 5:state 值改變後調用
this.setState(
{count:3},()=>{
//獲得結果作某種事
}
)
複製代碼
1.16.x 以前使用componentWillReveiveProps
componentWillReceiveProps (nextProps){
if(this.props.visible !== nextProps.visible){
//props 值改變作的事
}
}
複製代碼
注意:有些時候componentWillReceiveProps在 props 值未變化也會觸發,由於在生命週期的第一次render後不會被調用,可是會在以後的每次render中被調用 = 當父組件再次傳送props
2.16.x 以後使用getDerivedStateFromProps,16.x 之後componentWillReveiveProps也未移除
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 (
<div>
<div>
<Button type="primary" onClick={this.clickOne.bind(this)}>點擊加 1</Button><span>countOne 值爲{countOne}</span>
<div>{changeFlag}</div>
</div>
</div>
);
}
}
複製代碼
方式 1:ES5 的Function 定義
function FunCom(props){
return <div>這是Function 定義的組件</div>
}
ReactDOM.render(<FunCom name="Sebastian" />, mountNode)
// 在 hooks 未出來以前,這個是定義無狀態組件的方法,如今有了 hooks 也能夠處理狀態
複製代碼
方式 2: ES5的 createClass 定義
const CreateClassCom = React.createClass({
render: function() {
return <div>這是React.createClass定義的組件</div>
}
});
複製代碼
方式 3:ES6 的 extends
class Com extends React.Component {
render(){
return(<div>這是React.Component定義的組件</div>)
}
}
複製代碼
調用
export default class Seven extends React.Component {
render() {
return (
<div>
<FunCom></FunCom>
<Com></Com>
</div>
);
}
}
複製代碼
區別: ES5的 createClass是利用function模擬class的寫法作出來的es6; 經過es6新增class的屬性建立的組件此組件建立簡單.
方式 1:也是最先的用法,經過 this.refs[屬性名獲取] 也能夠做用到組件上,從而拿到組件實例
class RefOne extends React.Component{
componentDidMount() {
this.refs['box'].innerHTML='這是 div 盒子,經過 ref 獲取'
}
render(){
return(
<div ref="box"></div>
)
}
}
複製代碼
方式 2:回調函數,在dom節點或組件上掛載函數,函數的入參是dom節點或組件實例,達到的效果與字符串形式是同樣的,都是獲取其引用
class RefTwo extends React.Component{
componentDidMount() {
this.input.value='這是輸入框默認值';
this.input.focus();
}
render(){
return(
<input ref={comp => { this.input = comp; }}/>
)
}
}
複製代碼
方式 3:React.createRef() React 16.3版本後,使用此方法來建立ref。將其賦值給一個變量,經過ref掛載在dom節點或組件上,該ref的current屬性,將能拿到dom節點或組件的實例
class RefThree extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
componentDidMount(){
console.log(this.myRef.current);
}
render(){
return <input ref={this.myRef}/>
}
}
複製代碼
方式 4:React.forwardRef
React 16.3版本後提供的,能夠用來建立子組件,以傳遞ref
class RefFour extends React.Component{
constructor(props){
super(props);
this.myFourRef=React.createRef();
}
componentDidMount(){
console.log(this.myFourRef.current);
}
render(){
return <Child ref={this.myFourRef}/>
}
}
複製代碼
子組件經過React.forwardRef來建立,能夠將ref傳遞到內部的節點或組件,進而實現跨層級的引用。forwardRef在高階組件中能夠獲取到原始組件的實例.這個功能在技巧 18 會着重講
場景:聲明靜態方法的關鍵字,靜態方法是指即便沒有組件實例也能夠直接調用
export default class Nine extends React.Component {
static update(data) {
console.log('靜態方法調用執行啦')
}
render() {
return (
<div>
這是 static 關鍵字技能
</div>
);
}
}
Nine.update('2')
複製代碼
注意: 1.ES6的class,咱們定義一個組件的時候一般是定義了一個類,而static則是建立了一個屬於這個類的屬性或者方法
2.組件則是這個類的一個實例,component的props和state是屬於這個實例的,因此實例還未建立
3.因此static並非react定義的,而加上static關鍵字,就表示該方法不會被實例繼承,而是直接經過類來調用,因此也是沒法訪問到 this
4.getDerivedStateFromProps也是經過靜態方法監聽值,詳情請見技巧 6
回顧:
1.談這兩個屬性以前,先回顧一下ES6 函數定義方法
2.每個使用class方式定義的類默認都有一個constructor函數, 這個函數是構造函數的主函數, 該函數體內部的this指向生成的實例
3.super關鍵字用於訪問和調用一個對象的父對象上的函數
export default class Ten extends React.Component {
constructor() { // class 的主函數
super() // React.Component.prototype.constructor.call(this),其實就是拿到父類的屬性和方法
this.state = {
arr:[]
}
}
render() {
return (
<div>
這是技巧 10
</div>
);
}
}
複製代碼
場景:檢測傳入子組件的數據類型
類型檢查PropTypes自React v15.5起已棄用,請使用prop-types
方式 1:舊的寫法
class PropTypeOne extends React.Component {
render() {
return (
<div>
<div>{this.props.email}</div>
<div>{this.props.name}</div>
</div>
);
}
}
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 + "不符合郵箱的格式"
);
}
},
};
複製代碼
方法 2:利用 ES7 的靜態屬性關鍵字 static
class PropTypeTwo extends React.Component {
static propTypes = {
name:PropTypes.string
};
render() {
return (
<div>
<div>{this.props.name}</div>
</div>
);
}
}
複製代碼
場景:能夠在不使用構造函數的狀況下初始化本地狀態,並經過使用箭頭函數聲明類方法,而無需額外對它們進行綁定
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>
)
}
}
複製代碼
1.場景:路由切換,若是同步加載多個頁面路由會致使緩慢
2.核心 API:
loader:須要加載的組件
loading:未加載出來的頁面展現組件
delay:延遲加載時間
timeout:超時時間
3.使用方法:
安裝 react-loadable ,babel插件安裝 syntax-dynamic-import. react-loadable是經過webpack的異步import實現的
const Loading = () => {
return <div>loading</div>;
};
const LoadableComponent = Loadable({
loader: () => import("../../components/TwoTen/thirteen"),
loading: Loading
});
export default class Thirteen extends React.Component {
render() {
return <LoadableComponent></LoadableComponent>;
}
}
複製代碼
4.Loadable.Map()
並行加載多個資源的高階組件
場景:作一個 tab 切換時就會涉及到組件動態加載
實質上是利用三元表達式判斷組件是否顯示
class FourteenChildOne extends React.Component {
render() {
return <div>這是動態組件 1</div>;
}
}
class FourteenChildTwo extends React.Component {
render() {
return <div>這是動態組件 2</div>;
}
}
export default class Fourteen extends React.Component {
state={
oneShowFlag:true
}
tab=()=>{
this.setState({oneShowFlag:!this.state.oneShowFlag})
}
render() {
const {oneShowFlag} = this.state
return (<div>
<Button type="primary" onClick={this.tab}>顯示組件{oneShowFlag?2:1}</Button>
{oneShowFlag?<FourteenChildOne></FourteenChildOne>:<FourteenChildTwo></FourteenChildTwo>}
</div>);
}
}
複製代碼
若是是單個組件是否顯示能夠用短路運算
oneShowFlag&&<FourteenChildOne></FourteenChildOne>
複製代碼
場景:tree組件
利用React.Fragment或者 div 包裹循環
class Item extends React.Component {
render() {
const list = this.props.children || [];
return (
<div className="item">
{list.map((item, index) => {
return (
<React.Fragment key={index}>
<h3>{item.name}</h3>
{// 當該節點還有children時,則遞歸調用自己
item.children && item.children.length ? (
<Item>{item.children}</Item>
) : null}
</React.Fragment>
);
})}
</div>
);
}
}
複製代碼
受控組件:組件擁有本身的狀態
class Controll extends React.Component {
constructor() {
super();
this.state = { value: "這是受控組件默認值" };
}
render() {
return <div>{this.state.value}</div>;
}
}
複製代碼
不受控組件:組件無本身的狀態,在父組件經過 ref 來控制或者經過 props 傳值
class NoControll extends React.Component {
render() {
return <div>{this.props.value}</div>;
}
}
複製代碼
導入代碼:
export default class Sixteen extends React.Component {
componentDidMount() {
console.log("ref 獲取的不受控組件值爲", this.refs["noControll"]);
}
render() {
return (
<div>
<Controll></Controll>
<NoControll
value={"這是不受控組件傳入值"}
ref="noControll"
></NoControll>
</div>
);
}
}
複製代碼
就是相似高階函數的定義,將組件做爲參數或者返回一個組件的組件
1.屬性代理
import React,{Component} from 'react';
const Seventeen = WraooedComponent =>
class extends React.Component {
render() {
const props = {
...this.props,
name: "這是高階組件"
};
return <WrappedComponent {...props} />;
}
};
class WrappedComponent extends React.Component {
state={
baseName:'這是基礎組件'
}
render() {
const {baseName} = this.state
const {name} = this.props
return <div>
<div>基礎組件值爲{baseName}</div>
<div>經過高階組件屬性代理的獲得的值爲{name}</div>
</div>
}
}
export default Seventeen(WrappedComponent)
複製代碼
2.反向繼承
原理就是利用 super 改變改組件的 this 方向,繼而就能夠在該組件處理容器組件的一些值
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 <div>
<div>基礎組件值爲{baseName}</div>
</div>
}
}
export default Seventeen(WrappedComponent);
複製代碼
通常用三元表達式
flag?<div>顯示內容</div>:''
flag&&<div>顯示內容</div>
複製代碼
Dialog 應該是用的比較多的組件,下面有三種不一樣的建立方法 方式 1:經過 state 控制組件是否顯示
class NineteenChildOne extends React.Component {
render() {
const Dialog = () => <div>這是彈層1</div>;
return this.props.dialogOneFlag && <Dialog />;
}
}
複製代碼
方式 2:經過ReactDom.render建立彈層-掛載根節點外層
經過原生的createElement,appendChild, removeChild和react 的ReactDOM.render,ReactDOM.unmountComponentAtNode來控制元素的顯示和隱藏
NineteenChild.jsx
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();
}
};
複製代碼
nineteen.jsx
twoSubmit=()=>{
Dialog.show('這是彈層2')
}
twoCancel=()=>{
Dialog.hide()
}
複製代碼
做用:當類組件的輸入屬性相同時,可使用 pureComponent 或 shouldComponentUpdate 來避免組件的渲染。如今,你能夠經過把函數組件包裝在 React.memo 中來實現相同的功能
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 <div>{props.val}</div>;
}, areEqual);
複製代碼
做用:
1.React.PureComponent 和 React.Component相似,都是定義一個組件類。
2.不一樣是React.Component沒有實現shouldComponentUpdate(),而 React.PureComponent經過props和state的淺比較實現了。
3.React.PureComponent是做用在類中,而React.memo是做用在函數中。
4.若是組件的props和state相同時,render的內容也一致,那麼就可使用React.PureComponent了,這樣能夠提升組件的性能
class TwentyOneChild extends React.PureComponent{ //組件直接繼承React.PureComponent
render() {
return <div>{this.props.name}</div>
}
}
export default class TwentyOne extends React.Component{
render(){
return (
<div>
<TwentyOneChild name={'這是React.PureComponent的使用方法'}></TwentyOneChild>
</div>
)
}
}
複製代碼
做用:是基於ES6 class的React組件,React容許定義一個class或者function做爲組件,那麼定義一個組件類,就須要繼承React.Component
export default class TwentyTwo extends React.Component{ //組件定義方法
render(){
return (
<div>這是技巧22</div>
)
}
}
複製代碼
定義:
1.falsy 值 (虛值) 是在 Boolean 上下文中認定爲 false 的值;
2.值有 0,"",'',``,null,undefined,NaN
export default class TwentyThree extends React.Component{
state={myVariable:null}
render(){
return (
<div>{String(this.state.myVariable)}</div>
)
}
}
複製代碼
虛值若是直接展現,會發生隱式轉換,爲 false,因此頁面不顯示
做用:組件的render函數返回的元素會被掛載在它的父級組件上,createPortal 提供了一種將子節點渲染到存在於父組件之外的 DOM 節點的優秀的方案
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 (
<div className="modal">
這個是經過ReactDOM.createPortal建立的內容
</div>
);
}
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 (
<div>
<Button onClick={this.handleClick}>點擊加1</Button>
<p>點擊次數: {this.state.clicks}</p>
<Modal>
<Child />
</Modal>
</div>
);
}
}
複製代碼
這樣元素就追加到指定的元素下面啦
場景:有些後臺返回是 html 格式字段,就須要用到 innerHTML 屬性
export default class TwentyFive extends React.Component {
render() {
return (
<div dangerouslySetInnerHTML={{ __html: "<span>這是渲染的 HTML 內容</span>" }}></div>
);
}
}
複製代碼
語法:
React.createElement( type, [props], [...children] )
源碼:
export default class TwentySix extends React.Component {
render() {
return (
<div>
{React.createElement(
"div",
{ id: "one", className: "two" },
React.createElement("span", { id: "spanOne" }, "這是第一個 span 標籤"),
React.createElement("br"),
React.createElement("span", { id: "spanTwo" }, "這是第二個 span 標籤")
)}
</div>
);
}
}
複製代碼
原理:實質上 JSX 的 dom 最後轉化爲 js 都是React.createElement
// jsx 語法
<div id='one' class='two'>
<span id="spanOne">this is spanOne</span>
<span id="spanTwo">this is spanTwo</span>
</div>
// 轉化爲 js
React.createElement(
"div",
{ id: "one", class: "two" },
React.createElement( "span", { id: "spanOne" }, "this is spanOne"),
React.createElement("span", { id: "spanTwo" }, "this is spanTwo")
);
複製代碼
語法:
React.cloneElement(
element,
[props],
[...children]
)
複製代碼
做用:這個方法的做用是複製組件,給組件傳值或者添加屬性
核心代碼
React.Children.map(children, child => {
return React.cloneElement(child, {
count: _this.state.count
});
});
複製代碼
做用:React.Fragment可讓你聚合一個子元素列表,而且不在DOM中增長額外節點
核心代碼
render() {
const { info } = this.state;
return (
<div>
{info.map((item, index) => {
return (
<React.Fragment key={index}>
<div>{item.name}</div>
<div>{item.age}</div>
</React.Fragment>
);
})}
</div>
);
}
複製代碼
內部沒有封裝像 vue 裏面 v-for 的指令,而是經過 map 遍歷
{arr.map((item,index)=>{
return(
<div key={item.id}>
<span>{item.name}</span>
<span>{item.age}</span>
</div>
)
})}
複製代碼
做用:有些要經過自定義屬性傳值
export default class Thirty extends React.Component {
click = e => {
console.log(e.target.getAttribute("data-row"));
};
render() {
return (
<div>
<div data-row={"屬性1"} data-col={"屬性 2"} onClick={this.click}>
點擊獲取屬性
</div>
</div>
);
}
}
複製代碼
場景:交互就會涉及到事件點擊,而後點擊選中值傳參也是一個很常見場景
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 (
<div>
<Button type="primary" onClick={this.click.bind(this,'參數 1','參數 2')}>點擊事件</Button>
</div>
);
}
}
複製代碼
使用方法在源碼 routes.js 有詳細使用
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,在
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>
)
}
}
複製代碼
V4是經過 Route 嵌套,實現 Layout 和 page 嵌套,Switch切換路由的做用
方式 1:import 導入
import './App.css';
複製代碼
方式 2:內聯方式
import React from 'react';
const Header = () => {
const heading = '頭部組件'
return(
<div style={{backgroundColor:'orange'}}>
<h1>{heading}</h1>
</div>
)
}
或者
import React from 'react';
const footerStyle = {
width: '100%',
backgroundColor: 'green',
padding: '50px',
font: '30px',
color: 'white',
fontWeight: 'bold'
}
export const Footer = () => {
return(
<div style={footerStyle}>
底部組件
</div>
)
}
複製代碼
原理:經過三元表達式控制 className 值
render(){
const flag=true
return (
<div className={flag?"active":"no-active"}>這是技巧 34</div>
)
}
複製代碼
這就是從實際項目開發總結的 React的 34 個技巧;
源碼地址,請戳 歡迎 star