React 是前端三大框架之一,在面試和開發中也是一項技能;
本文從實際開發中總結了 React 開發的一些技巧技巧,適合 React 初學或者有必定項目經驗的同窗;
萬字長文,建議收藏。
序列文章:Vue 開發必須知道的 36 個技巧【近1W字】css
請戳,歡迎 star html
效果圖
前端
子組件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 children = this.props.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 是一個獨立的事件通信插件,這裏就不作過多的敘述
mbox 也是一個獨立的事件通信插件,這裏就不作過多的敘述
mobox 也是一個獨立的事件通信插件,這裏就不作過多的敘述
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)
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>); } }
場景: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>:''
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> ); } }
爲何是危險的屬性,由於這個會致使 XSS 攻擊
_html是兩個
語法:
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> ); }
場景:交互就會涉及到事件點擊,而後點擊選中值傳參也是一個很常見場景
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> ); } }
做用:有些要經過自定義屬性傳值
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> ); } }
內部沒有封裝像 vue 裏面 v-for 的指令,而是經過 map 遍歷
使用方法在源碼 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 的,這就意味着多個 <Route>能夠同時匹配和呈現.若是隻想匹配一個路由,可使用Switch,在 <Switch> 中只有一個 <Route> 會被渲染,同時能夠再在每一個路由添加exact,作到精準匹配
Redirect,瀏覽器重定向,當多有都不匹配的時候,進行匹配
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;
源碼地址,請戳,歡迎 star