為了提高代碼的復用在react中我們可以使用高階組件
1.添加高階組件
高階組件主要代碼模板HOC.js
export default (WrappedComponent) => { return class extends Component { constructor(props) { super(props) this.state = { //定義可復用的狀態 } this.getCode = this.getCode.bind(this) } componentWillMount() { } //定義可復用的方法 getCode(mobile) { ... } postVcode(mobile) { ... } render() { return ( <div> <WrappedComponent getCode={this.getCode} state={this.state} {...this.props}/> </div> ) } } }
注:其中<WrappedComponent />的自定義屬性getCode與state傳遞了對外可以用的方法與屬性
2.在其他組件中使用高階組件
register.js
import HOC from 'common/js/HOC' class Register extends Component{ ... ... } export default HOC(Register)
或者也可以使用裝飾器語法書寫
import HOC from 'common/js/HOC' @HOC class Register extends Component{ ... } export default Register
3.完整示例代碼
例如 發送短信驗證碼功能在注冊於忘記密碼2個組件中都用到了,我們可以把它抽離到HOC中
HOC.js

import React, {Component} from 'react' import axios from 'axios' import qs from 'qs' import { noToken } from './config' import { loadToken } from 'common/js/cache' import { url } from 'common/js/config' let token = loadToken() console.log('token', token); export default (WrappedComponent) => { return class extends Component { constructor(props) { super(props) this.state = { codeBtnText: '發送驗證碼' } this.getCode = this.getCode.bind(this) this.postVcode = this.postVcode.bind(this) } componentWillMount() { } getCode(mobile) { if (mobile === '') { this.setState({ tipsText: '請輸入手機號碼' }) this.refs.confirm.show() return } if (!/1[3|4|5|7|8]\d{9}/.test(mobile)) { this.setState({ tipsText: '請輸入正確手機號碼' }) this.refs.confirm.show() return } this.codeBtnDisable = true // console.log(this.codeBtnDisable) this.postVcode(mobile) let total = 59 this.setState({codeBtnText: `${total}秒`}) // this.codeBtnText = `${total}秒` this.timer = setInterval(() => { // console.log(total) --total // this.codeBtnText = `${total}秒` this.setState({codeBtnText: `${total}秒`}) if (total <= 0) { clearInterval(this.timer) // this.codeBtnText = '發送驗證碼' this.setState({codeBtnText: `發送驗證碼`}) this.codeBtnDisable = false } }, 1000) } postVcode(mobile) { var _params = { Phone: mobile } var that = this axios({ method: 'post', url: url + 'ComService/PostVcode', data: _params }) .then(function (res) { if (res.data.issuccess) { that.setState({ tipsText: res.data.message }) that.refs.confirm.show() //that.saveUserApplyStepList(res.data.result) } else { that.setState({ tipsText: res.data.message }) that.refs.confirm.show() that.codeBtnDisable = false clearInterval(that.timer) // that.codeBtnText = `發送驗證碼` this.setState({codeBtnText: `發送驗證碼`}) return false } }) .catch(function (error) { console.log(error) }) } render() { return ( <div> <WrappedComponent getCode={this.getCode} state={this.state} {...this.props}/> </div> ) } } }
register.js

import React, {Component} from 'react' import axios from 'common/js/http' import { url } from 'common/js/config' import Confirm from 'base/confirm/confirm' import { withRouter } from 'react-router-dom' import { setToken } from 'common/js/cache' import getCodeHOC from 'common/js/HOC' @getCodeHOC class Register extends Component{ constructor() { super() this.state = { mobile: '', password: '', tipsText: '', isOpen: false, inputType: 'password', codeBtnText: '發送驗證碼' } this.confirm = React.createRef() this.buttonBtn = React.createRef() this.handleMobileChange = this.handleMobileChange.bind(this) this.handlePassWordChange = this.handlePassWordChange.bind(this) this.handleLogin = this.handleLogin.bind(this) this.switchEye = this.switchEye.bind(this) this.goToLogin = this.goToLogin.bind(this) // this.getCode = this.getCode.bind(this) this.btnDisable = false this.codeBtnDisable = false // this.codeBtnText = '發送驗證碼' this.timer = null } componentDidMount() { let _mobile = localStorage.getItem('mobile') this.setState({ mobile: _mobile }) } handleMobileChange (e) { this.setState({ mobile: e.target.value }) } handleLogin (e) { let that = this let params = { Identifier:this.state.mobile, Credential:this.state.password } if (params.Identifier === '') { this.setState({ tipsText:'請輸入手機號碼' }) this.refs.confirm.show() return } if (!/1[3|4|5|7|8]\d{9}/.test(params.Identifier)) { this.setState({ tipsText:'請輸入正確手機號碼' }) this.refs.confirm.show() return } if (params.Credential === '') { this.setState({ tipsText:'請輸入密碼' }) this.refs.confirm.show() return false } axios({ method: 'post', url: url + 'Login/UserLogin', data: params }).then((res) => { // console.log(res); if (res.data.issuccess) { console.log(that.props); window.location.href = '/home' setToken(res.data.result.Token) localStorage.setItem('mobile', that.state.mobile) } else { that.setState({ tipsText: res.data.message }) that.refs.confirm.show() } }) } handlePassWordChange(e) { this.setState({ password: e.target.value }) } switchEye () { this.setState(prevState => ({ isOpen: !prevState.isOpen }), ()=> { this.state.isOpen ? this.setState({inputType: 'text'}) : this.setState({inputType: 'password'}) }) } shouldComponentUpdate(nextProps, nextState) { // console.log('nextProps', nextProps); console.log('nextState', nextState); if (nextState.mobile && (/1[3|4|5|7|8]\d{9}/.test(nextState.mobile)) && nextState.password ) { this.btnDisable = true } else { this.btnDisable = false } if (nextState.codeBtnText === '發送驗證碼') { this.codeBtnDisable = false } return true } goToLogin () { console.log(this.props); this.props.history.push('/login') } render() { return ( <div> <div className="title">用戶注冊</div> <div className="inputBox padType"> <div className="input size-0 align-1"> <label >手機號</label> <input type="tel" placeholder="請輸入手機號" maxLength="11" value={this.state.mobile} onChange={this.handleMobileChange} /> </div> </div> <div className="inputBox padType"> <div className="input size-0 align-1"> <label >驗證碼</label> <input placeholder="請輸入密碼" maxLength="6" type="number"/> <div className={"send-smg-code " + (this.codeBtnDisable ? 'disable' : '')} onClick={()=>this.props.getCode(this.state.mobile)}>{this.props.state.codeBtnText}</div> </div> </div> <div className="inputBox padType"> <div className="input size-0 align-1"> <label >密 碼</label> <input placeholder="請輸入密碼" maxLength="16" type={this.state.inputType} onChange={this.handlePassWordChange}/> <div className="eye" onClick={this.switchEye}> </div> </div> </div> <div className="agree-register"> <div className="agree-check"></div> <div className="agree-text"> 同意《 <a href="#/useAgreementRegister"> 注冊協議 </a> 》 </div> </div> <div className="buttonBox disable"> <div className={"button " + (this.btnDisable ? '' : 'disable')}> 注冊 </div> </div> <div className="child-mt20"> <div className="buttonBox reverse"> <div className="button reverse" onClick={this.goToLogin}> 已有賬號,去登錄 </div> </div> </div> <Confirm text={this.state.tipsText} confirmType='2' ref="confirm"></Confirm> </div> ) } } export default withRouter(Register)
forgetpassword.js

import React, {Component} from 'react' import axios from 'common/js/http' import { url } from 'common/js/config' import Confirm from 'base/confirm/confirm' import { withRouter } from 'react-router-dom' import { setToken } from 'common/js/cache' import HOC from 'common/js/HOC' // @HOC class Forgetpassword extends Component{ constructor() { super() this.state = { mobile: '', password: '', tipsText: '', isOpen: false, inputType: 'password', codeBtnText: '發送驗證碼' } this.confirm = React.createRef() this.buttonBtn = React.createRef() this.handleMobileChange = this.handleMobileChange.bind(this) this.handlePassWordChange = this.handlePassWordChange.bind(this) this.handleLogin = this.handleLogin.bind(this) this.switchEye = this.switchEye.bind(this) // this.getCode = this.getCode.bind(this) this.btnDisable = false this.codeBtnDisable = false // this.codeBtnText = '發送驗證碼' this.timer = null } componentDidMount() { let _mobile = localStorage.getItem('mobile') this.setState({ mobile: _mobile }) } handleMobileChange (e) { this.setState({ mobile: e.target.value }) } handleLogin (e) { let that = this let params = { Identifier:this.state.mobile, Credential:this.state.password } if (params.Identifier === '') { this.setState({ tipsText:'請輸入手機號碼' }) this.refs.confirm.show() return } if (!/1[3|4|5|7|8]\d{9}/.test(params.Identifier)) { this.setState({ tipsText:'請輸入正確手機號碼' }) this.refs.confirm.show() return } if (params.Credential === '') { this.setState({ tipsText:'請輸入密碼' }) this.refs.confirm.show() return false } axios({ method: 'post', url: url + 'Login/UserLogin', data: params }).then((res) => { // console.log(res); if (res.data.issuccess) { console.log(that.props); // window.location.href = '/home' let _href = window.location.href let _hrefArr = _href.split('#') window.location.href = _hrefArr[0] + '#/home'; //太low了。。。。 setToken(res.data.result.Token) localStorage.setItem('mobile', that.state.mobile) } else { that.setState({ tipsText: res.data.message }) that.refs.confirm.show() } }) } handlePassWordChange(e) { this.setState({ password: e.target.value }) } switchEye () { this.setState(prevState => ({ isOpen: !prevState.isOpen }), ()=> { this.state.isOpen ? this.setState({inputType: 'text'}) : this.setState({inputType: 'password'}) }) } shouldComponentUpdate(nextProps, nextState) { // console.log('nextProps', nextProps); console.log('nextState', nextState); if (nextState.mobile && (/1[3|4|5|7|8]\d{9}/.test(nextState.mobile)) && nextState.password ) { this.btnDisable = true } else { this.btnDisable = false } if (nextState.codeBtnText === '發送驗證碼') { this.codeBtnDisable = false } return true } render() { return ( <div> <div className="title">忘記密碼</div> <div className="inputBox padType"> <div className="input size-0 align-1"> <label >手機號</label> <input type="tel" placeholder="請輸入手機號" maxLength="11" value={this.state.mobile} onChange={this.handleMobileChange}/> </div> </div> <div className="inputBox padType"> <div className="input size-0 align-1"> <label >驗證碼</label> <input placeholder="請輸入密碼" maxLength="6" type="number"/> <div className={'send-smg-code ' + (this.codeBtnDisable ? 'disable' : '')} onClick={(e) => this.props.getCode(this.state.mobile)}>{this.props.state.codeBtnText}</div> </div> </div> <div className="inputBox padType"> <div className="input size-0 align-1"> <label >新密碼</label> <input placeholder="請輸入密碼" maxLength="16" type={this.state.inputType} onChange={this.handlePassWordChange}/> <div className={ 'eye ' + (this.state.isOpen ? 'open' : '')} onClick={this.switchEye}></div> </div> </div> <div className="buttonBox "> <div className={'button ' + (this.btnDisable ? '': "disable")} ref={this.buttonBtn} onClick={this.handleLogin}> 確定 </div> </div> <Confirm text={this.state.tipsText} confirmType='2' ref="confirm"></Confirm> </div> ) } } export default withRouter(HOC(Forgetpassword))
4.注意點
一、使用裝飾器語法需要安裝transform-decorators-legacy插件並配置package
1.npm install babel-plugin-transform-decorators-legacy --save-dev
2.配置package中babel下plugin
"babel": { "presets": [ "react-app" ], "plugins": ["transform-decorators-legacy"] },
二、高階組件中的復用方法或狀態屬性需要在其他組件中使用記得作為屬性傳遞出去,以便其他組件使用