Recently, taro has been experimenting with taro multi-terminal development. Compared with UniApp multi-terminal technology, taro is much more complex to develop multi-terminal applications, especially when compiled into app.

Develop taro-Chatroom based on Taro+ React + Redux + Rn +taroPop



Taro Technology Stack:

  • Encoding/technology: vscode + react/taro/story/reactNative
  • Iconfont: Ali font icon library
  • Custom top navigation bar + Tabbar
  • Pop-up component: taroPop (Taro based Encapsulation custom dialog)
  • Support compilation: H5 + small program + App end

































Taro Custom navigation bar mode

In order to achieve a unified effect, the function of the project is relatively rich. The top navigation and the bottom tabbar adopt custom component mode. Due to the previous shared article, I will not introduce it in detail here.

See the following examples of the top navigation bar/bottom Tabbar components

See the Taro Custom Modal Components: Taro Custom Modal Box Components

Entry app.jsx page configuration

JSX * @about Q: 282310962 wx: xy190310 */ import Taro, {Component} from'@tarojs/taro'
import Index from './pages/index'Redux import {Provider} from'@tarojs/redux'
import { store } from './store'// Introduce style import'./app.scss'
import './styles/fonts/iconfont.css'
import './styles/reset.scss'

class App extends Component {
  config = {
    pages: [
      'pages/auth/login/index'.'pages/auth/register/index'.'pages/index/index'. ] , window: { backgroundTextStyle:'light',
      navigationBarBackgroundColor: '#fff',
      navigationBarTitleText: 'TaroChat',
      navigationBarTextStyle: 'black',
      navigationStyle: 'custom'} // The render() function in the App class has no effectrender () {
    return (
      <Provider store={store}>
        <Index />
      </Provider>
    )
  }
}

Taro.render(<App />, document.getElementById('app'))
Copy the code

Taro login/registration form validation | redux state management | local storage

It’s also easy to get form input values from taro, using the onInput event

<Input placeholder="Please enter mobile phone number/nickname" onInput={this.handleInput.bind(this, 'tel')} / >Copy the code

this.state = {
	tel: ' '.pwd: ' ',
}

handleInput = (key, e) => {
    this.setState({ [key]: e.detail.value })
}
Copy the code

It is easy to get the input value using the above method

return (
    <View className="taro__container flexDC bg-eef1f5">
        <Navigation background='#eef1f5' fixed />
        
        <ScrollView className="taro__scrollview flex1" scrollY>
            <View className="auth-lgreg">
                {/* logo */}
                <View className="auth-lgreg__slogan">
                    <View className="auth-lgreg__slogan-logo">
                        <Image className="auth-lgreg__slogan-logo__img" src={require('.. /.. /.. /assets/taro.png')} mode="aspectFit" />
                    </View>
                    <Text className="auth-lgreg__slogan-text"> </Text> </View> {/* form */} <View className="auth-lgreg__forms">
                    <View className="auth-lgreg__forms-wrap">
                        <View className="auth-lgreg__forms-item">
                            <Input className="auth-lgreg__forms-iptxt flex1" placeholder="Please enter mobile phone number/nickname" onInput={this.handleInput.bind(this, 'tel')} />
                        </View>
                        <View className="auth-lgreg__forms-item">
                            <Input className="auth-lgreg__forms-iptxt flex1" placeholder="Please enter your password" password onInput={this.handleInput.bind(this, 'pwd')} />
                        </View>
                    </View>
                    <View className="auth-lgreg__forms-action">
                        <TouchView onClick={this.handleSubmit}><Text className="auth-lgreg__forms-action__btn"</Text></TouchView> </View> <View className="auth-lgreg__forms-link">
                        <Text className="auth-lgreg__forms-link__nav"> Forget password </Text> <Text className="auth-lgreg__forms-link__nav"OnClick ={this.GoToRegister}> register the account </Text> </View> </View> </ScrollView> <TaroPop ref="taroPop" />
    </View>
)
Copy the code

/** * @tpl login module */ import Taro from'@tarojs/taro'
import { View, Text, ScrollView, Image, Input, Button } from '@tarojs/components'

import './index.scss'

import { connect } from '@tarojs/redux'
import * as actions from '.. /.. /.. /store/action'. class Login extends Taro.Component { config = { navigationBarTitleText:'login'
    }
    constructor(props) {
        super(props)
        this.state = {
            tel: ' '.pwd: ' ',}}componentWillMount() {// Check whether to log in to storage.get()'hasLogin').then(res => {
            if(res && res.hasLogin) {
                Taro.navigateTo({url: '/pages/index/index'// Submit the form handleSubmit = () => {let taroPop = this.refs.taroPop
        let { tel, pwd } = this.state

        if(! tel) { taroPop.show({content:'Mobile phone number cannot be empty', time: 2})
        }else if(! util.checkTel(tel)) { taroPop.show({content:'Wrong format of mobile phone number', time: 2})
        }else if(!pwd) {
            taroPop.show({content: 'Password cannot be empty', time: 2})
        }else{/ /... Interface data... storage.set('hasLogin', { hasLogin: true })
            storage.set('user', { username: tel })
            storage.set('token', { token: util.setToken() })

            taroPop.show({
                skin: 'toast',
                content: 'Login successful',
                icon: 'success', time: 2 }) ... }}render() {... } } const mapStateToProps = (state) => {return{... state.auth} }exportdefault connect(mapStateToProps, { ... actions })(Login)Copy the code

In addition, note that the TARO rn terminal does not support synchronous storage, and can only be changed to setStorageSync asynchronous storage



/** * @desc Taro local storage */ import Taro from'@tarojs/taro'

export default class Storage {
    static get(key) {
        return Taro.getStorage({ key }).then(res => res.data).catch(() => ' ')
    }

    static set(key, data){
        return Taro.setStorage({key: key, data: data}).then(res => res)
    }

    static del(key){
        Taro.removeStorage({key: key}).then(res => res)
    }

    static clear(){
        Taro.clearStorage()
    }
}
Copy the code

Style compatibility processing

For styles that do not want to compile to the RN side, wrap them as follows

/*postcss-pxtransform rn eject enable*//*postcss-pxtransform rn eject disable* /Copy the code

Rn incompatible styles can be handled uniformly through the @mixin function provided by SCSS

** * RN does not support setting a style on one side or the other. < span style = "border-bottom: 1px" style = "border-bottom: 1px"; border-bottom-width: 1px; */ @mixin border($dir, $width, $style, $color) { border: 0 $style $color; @each $d in $dir { #{border-#{$d}-width}: $width; }} /** * NOTE RN cannot implement ellipses through text-overflow, */ @mixin Ellipsis {/* postcss-pxTransform RN eject enable*/ overflow: hidden; white-space: nowrap; text-overflow: ellipsis; /* postcss-pxTransform rn eject disable*/} /** * NOTE H5/ miniprogram -webkit-line-clamp */ @mixin clamp($line) {/*postcss -pxTransform RN eject enable*/ display: -webkit-box; overflow: hidden; -webkit-line-clamp:$line; /* autoprefixer: ignore next */ -webkit-box-orient: vertical; /* postcss-pxTransform rn eject disable*/} /** * For styles that cannot be packaged into Rn, */ @mixin eject($attr, $value) {/* postcss-pxTransform RN eject enable*/ #{$attr}: $value; /*postcss-pxtransform rn eject disable*/ }Copy the code

Taro scroll to the bottom of a chat message



H5/ applet can scroll to the bottom of the chat by getting createSelectorQuery. Since RN does not support createSelectorQuery, it can only be processed in other ways.



componentDidMount() {
    if(process.env.TARO_ENV === 'rn') {
        this.scrollMsgBottomRN()
    }else {
        this.scrollMsgBottom()
    }
}Copy the code

// scrollMsgBottom = () => {let query = Taro.createSelectorQuery()
    query.select('#scrollview').boundingClientRect()
    query.select('#msglistview').boundingClientRect()
    query.exec((res) => {
        // console.log(res)
        if(res[1].height > res[0].height) {
            this.setState({ scrollTop: res[1].height - res[0].height })
        }
    })
}
scrollMsgBottomRN = (t) => {
    let that = this
    this._timer = setTimeout(() => {
        that.refs.ScrollViewRN.scrollToEnd({animated: false}) }, t ? 16:0)}Copy the code

The part of chat expression uses emoj emoji, which is relatively simple to implement and will not be introduced.

. MsgPanelClicked = () => {if(! this.state.showFootToolbar)return
	this.setState({ showFootToolbar: false} // swtEmojChooseView = (index) => {this.setState({showFootToolbar:true, showFootViewIndex: index})} swtEmojTab = (index) => {let lists = this.state.emotionJson
	for(var i = 0, len = lists.length; i < len; i++) {
		lists[i].selected = false
	}
	lists[index].selected = trueEnclosing setState ({emotionJson: lists})} / * > > > "editor/expression processing module" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /bindEditorInput = (e) => {
	this.setState({
		editorText: e.detail.value,
		editorLastCursor: e.detail.cursor
	})
}
bindEditorFocus = (e) => {
	this.setState({ editorLastCursor: e.detail.cursor })
}
bindEditorBlur = (e) => {
	this.setState({ editorLastCursor: e.detail.cursor })
}

handleEmotionTaped = (emoj) => {
	if(emoj == 'del') return// Insert the emoji at the cursorlet { editorText, editorLastCursor } = this.state
	let lastCursor = editorLastCursor ? editorLastCursor : editorText.length
	let startStr = editorText.substr(0, lastCursor)
	let endStr = editorText.substr(lastCursor)
	this.setState({
		editorText: startStr + `${emoj} ` + endStr
	})
}

...
Copy the code

Taro taro taro taro taro taro taro taro taro Taro Taro Taro Taro Taro 😴 😴

Vue + UniApp mimics Douyin short video/Momo live chat room

In pieces the react + redux imitation WeChat web chat end | web chat instance