When the mobile Internet entered the white-hot stage, a kind of “light application” represented by wechat small program sprang to prominence. They don’t need to download, are easy to use, “use up and go”, and have relatively complete functions. Once launched, they have been warmly welcomed by various platforms and users. But the problem is that developers have to maintain multiple user interfaces on the Web, mobile, wechat, Alipay, etc. The maintenance costs can be imagined. Taro’s emergence as an excellent multi-terminal unified development solution has changed that. As Taro 2.x enters beta, let’s make a cup of tea and start our Taro multiwidget development tour.

start

Taro is a boon for Chinese Developers of React. It allows us to build various apps using familiar React code, and one code can be compiled into multiple platform applications (currently including wechat applet, Alipay applet, React Native, H5, etc.). As Taro has evolved to support the React code and target platforms more and more, the value of learning goes without saying. As Taro enters the beta phase of version 2.0.0, in this tutorial we will take you through a step-by-step implementation of a small application that can be deployed to multiple applications to give you a taste of the power and power of Taro.

In this series of tutorials, we will build a multi-terminal applet, Ultraman Club (UltraClub), a tiebar-like applet that supports multi-terminal login (wechat and Alipay). We also provided the GitHub address for the project repository. The project is still under development, so you can jump to any COMMIT and see all the code for the current step.

What will we build?

After completing this tutorial, a GIF of the project looks like this:

The premise condition

Before reading this tutorial, we hope you have the following knowledge:

  • Know the basics of HTML, CSS, JavaScript, and even better Sass
  • Learn the basics of the React framework by following this tutorial. React Native and Hooks are even better
  • With Node and NPM installed, you can follow this tutorial

In addition, you also need to download and install the wechat developer tool, hereDownload address.

The source code for this article is available on Github. If you think we did a good job, please give ❤️ a thumbs up +Github repository + star ❤️

Initialize the project with Taro scaffolding

First install Taro CLI:

npm install -g @tarojs/cli
Copy the code

Then create our project:

taro init ultra-club
Copy the code

Then there will be a series of options, you can choose according to the following figure (CSS preprocessor select Sass, template select “default template”, veteran drivers can choose to use TS) :

prompt

This project uses Sass mainly for compatibility with the taro-UI style, and does not use the advanced features of Sass. If you are not familiar with them, don’t worry, just take it as normal CSS code.

After going into our project directory ultra-Club, you can see that the project template includes the following files:

.├ ─ config # ├─ dev.js # ├─ index.js # ├─ ├─ package.json ├─ └─ SRC # ├─ app.scss # Root Component Style ├─ app.jsx # Root component App ├─ index.html # Waiting to be embedded code ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ index.jsx # indexCopy the code

We look at the two main code files: SRC/app. The JSX and SRC/pages/index/index. The JSX.

A preliminary study of scaffolding code

SRC /app.jsx defines the root component of the project, app, as follows:

import Taro, { Component } from '@tarojs/taro'
import Index from './pages/index'

import './app.scss'

// If you need to enable React Devtools in the H5 environment
// Cancel the following comment:
// if (process.env.NODE_ENV !== 'production' && process.env.TARO_ENV === 'h5')  {
// require('nerv-devtools')
// }

class App extends Component {
  config = {
    pages: ['pages/index/index'].window: {
      backgroundTextStyle: 'light'.navigationBarBackgroundColor: '#fff'.navigationBarTitleText: 'WeChat'.navigationBarTextStyle: 'black',}}// The render() function in the App class has no real effect
  // Do not modify this function
  render() {
    return <Index />
  }
}

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

If you’re familiar with React, this code is easy to understand. It just changes the React and ReactDOM locations to Taro.

Pay attention to

You can see that this component also has a config property, which is specific to applet applications. Which is to focus on the array of pages, the page lists all modules, such as the pages/index/index corresponds to the SRC/pages/index/index. The JSX. The Pages attribute will also be used later when implementing routes.

We’ll look at the SRC/pages/index/index. The JSX. SRC /pages/index is the index page component module. The code for index.jsx is as follows:

import Taro, { Component } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import './index.scss'

export default class Index extends Component {
  config = {
    navigationBarTitleText: 'home',
  }

  render() {
    return (
      <View className="index">
        <Text>Hello world!</Text>
      </View>)}}Copy the code

The React component is the same as the React component, but instead of using div and P tags in the render function, Taro has prepared the View and Text components for us. Why should Taro build his own component library? Because Taro’s goal is the sea of stars… Sorry, is able to compile to all platforms. Only through the development of Taro’s own component library, can we build an abstraction layer on top of the native component library of each platform to achieve the goal of cross-platform.

prompt

If you have experience developing React Native, you are no stranger to the Taro component library.

Run applets

The template code provided by Taro can be run directly. Open the terminal and run the following command:

npm run dev:weapp
Copy the code

The following message is displayed:

When you see “Listening for file changes… After the prompt, we can open the wechat developer tool, and log in with the wechat scan code, the interface is as follows:

Click the big ➕ number to start importing the ultra-Club project we just created:

As shown in the image above, first switch to the “Import Project” column, then click the button on the right of the “Directory” input field to select the ultra-Club folder you just created, and finally click the “Import” button in the lower right corner.

After the successful import, the interface of wechat developer tool is as follows:

In the emulator page, you see the Hello World rendered by our Index page; The editor can see all the code, but usually we develop with the code editor we’re used to (VSCode is awesome!). ; The debugger is a developer tool similar to Chrome.

Everything is ready, let’s get to work!

prompt

From this step on, our main development target will be the wechat applet, but don’t worry, we will show how to compile to other platforms in the next tutorial.

React code, familiar smell

From this step, we will implement the “Ultraman Club” small program. Following the React concept of “Everything is a component”, we abstracted two components:

  • PostCard: Used to display a post, including the titletitleAnd the contentcontent
  • PostForm: A form for Posting new posts

Implementing the PostCard component

Start by creating the SRC/Components directory where our generic components will be stored. Then create the SRC/Components /PostCard component directory, where you can create index.jsx and index.scss, respectively. The index.jsx code is as follows:

import Taro from '@tarojs/taro'
import { View } from '@tarojs/components'

import './index.scss'

export default function PostCard(props) {
  return (
    <View className="postcard">
      <View className="post-title">{props.title}</View>
      <View className="post-content">{props.content}</View>
    </View>)}Copy the code

As mentioned earlier, the PostCard component contains two props: Title and Content.

The code for the PostCard component’s style index. SCSS is as follows:

.postcard {
  margin: 30px;
  padding: 20px;
  border: 1px solid #ddd;
}

.post-title {
  font-weight: bolder;
  margin-bottom: 10px;
}

.post-content {
  font-size: medium;
  color: # 666;
}
Copy the code

Implement the PostForm component

Next we implement the PostForm component for creating a new post. Create the PostForm directory in SRC/Components and add the index.jsx and index.scss files to it. The index.jsx code is as follows:

import Taro from '@tarojs/taro'
import { View, Form, Input, Textarea, Button } from '@tarojs/components'

import './index.scss'

export default function PostForm(props) {
  return</View> < form onSubmit={props. HandleSubmit}> <View> <View ClassName ="form-hint"> title </View> <Input className="input-title" type="text" placeholder=" value={props. FormTitle} OnInput ={props. HandleTitleInput} /> <View className="form-hint"> </View> <Textarea placeholder= className="input-content" value={props.formContent} onInput={props.handleContentInput} /> <Button </ button > </ form > </View>)}Copy the code

The PostForm component defines five props, as follows:

  • formTitle: Title of the post in the current edit
  • formContent: The content of the post in the current edit
  • handleSubmit: the callback function that handles the submitted form
  • handleTitleInput: Handles the callback function when the header receives user input
  • handleContentInput: Handles the callback function when content receives user input

prompt

If you’re not familiar with React, you might be a little confused by the way forms are written above. In fact, React recommends writing forms as “controlled components.” See this document.

The PostForm style file index. SCSS looks like this:

.post-form {
  border: 1px solid #ddd;
  margin: 30px;
  padding: 30px;
}

.input-title {
  border: 1px solid #eee;
  padding: 10px;
  font-size: medium;
}

.input-content {
  border: 1px solid #eee;
  padding: 10px;
  height: 200px;
  font-size: medium;
}

.form-hint {
  font-size: small;
  color: gray;
  margin-top: 20px;
  margin-bottom: 10px;
}

.form-button {
  margin-top: 40px;
}
Copy the code

To make it easier to use the PostCard and PostForm components in your page components, we turned SRC/Components into a module. Specifically, create SRC /components/index.jsx as follows:

import PostCard from './PostCard'
import PostForm from './PostForm'

export { PostCard, PostForm }
Copy the code

Access the PostCard and PostForm in the Index page

Finally in the SRC/pages/index/index. The JSX file before written it and PostForm components, the code is as follows:

import Taro, { Component } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { PostCard, PostForm } from '.. /.. /components'
import './index.scss'

export default class Index extends Component {
  state = {
    posts: [{title: 'Telo Altman'.content: 'Tiro was the only biological son of the father and mother of Otter. '],},formTitle: ' '.formContent: ' ',
  }

  config = {
    navigationBarTitleText: 'home',
  }

  handleSubmit(e) {
    e.preventDefault()

    const { formTitle: title, formContent: content } = this.state
    const newPosts = this.state.posts.concat({ title, content })

    this.setState({
      posts: newPosts,
      formTitle: ' '.formContent: ' ',
    })
  }

  handleTitleInput(e) {
    this.setState({
      formTitle: e.target.value,
    })
  }

  handleContentInput(e) {
    this.setState({
      formContent: e.target.value,
    })
  }

  render() {
    return (
      <View className="index">
        {this.state.posts.map((post, index) => (
          <PostCard key={index} title={post.title} content={post.content} />
        ))}
        <PostForm
          formTitle={this.state.formTitle}
          formContent={this.state.formContent}
          handleSubmit={e => this.handleSubmit(e)}
          handleTitleInput={e => this.handleTitleInput(e)}
          handleContentInput={e => this.handleContentInput(e)}
        />
      </View>
    )
  }
}
Copy the code

As you can see, in addition to the two components we defined earlier, we have added some states:

  • posts: All current posts, each post is a containtitlecontentThe object of
  • formTitle: Title of the post currently being edited
  • formContent: The content of the current post being edited

And defines the three callback functions needed in the PostForm component.

See the effect

If the developer server is still active, the app developer tools should be able to see the effects directly. (If the server was disabled, you can run NPM Run dev: Retry.)

Pay attention to

Sometimes Taro may have a style loading failure. If you do, shut down the development server and restart NPM Run Dev: Pervasive.

Pack light

The front end circle has certainly experienced an earthquake since the React team introduced Hooks in the 2018 React Conf. With just a few apis, you can easily manage component state management and data flow in a purely functional way. What kind of magic operation is this?

Fortunately, the Taro team also added support for Hooks in v1.3.0. Therefore, we will also address state management and data flow issues with Hooks in this project.

A quick review of useState from Hooks

Here is a brief overview of useState Hook. If you are already familiar with it, please move on to the hands-on section below.

For example, we had a class component called ClickMe that complained about how many times you clicked on it:

class ClickMe extends Component {
  state = { count: 0 }

  render() {
    return (
      <div>
        <button onClick={()= >This.setstate ({count: this.state.count + 1})}> You click on {this.state.count} times!</button>
      </div>)}}Copy the code

This becomes a functional component using Hooks:

// Import the useState function
import Taro, { useState } from '@tarojs/taro'

function ClickMe() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <button onClick={()= >SetCount (count + 1)}> You click me {count} times!</button>
    </div>)}Copy the code

As you can see, the useState function returns two values:

  • state(That’s what’s up therecount) : can be used directly during rendering
  • A function that modifies state(That’s what’s up theresetCount) : Used to update the state by passing in a new state while processing the corresponding event

Also notice that useState takes one parameter, the initial value of the state. Here we take a Number, which can actually be a string, an array, an object, etc.

To link

To get hands-on, we used useState to refactor our index page. Specifically, we converted the entire Index component into a functional component, and then created the first three states using useState as follows:

import Taro, { useState } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { PostCard, PostForm } from '.. /.. /components'
import './index.scss'

export default function Index() {
  const [posts, setPosts] = useState([
    {
      title: 'Telo Altman'.content: 'Tiro was the only biological son of the father and mother of Otter. ',}])const [formTitle, setFormTitle] = useState(' ')
  const [formContent, setFormContent] = useState(' ')

  function handleSubmit(e) {
    e.preventDefault()

    const newPosts = posts.concat({ title: formTitle, content: formContent })
    setPosts(newPosts)
    setFormTitle(' ')
    setFormContent(' ')}return (
    <View className="index">
      {posts.map((post, index) => (
        <PostCard key={index} title={post.title} content={post.content} />
      ))}
      <PostForm
        formTitle={formTitle}
        formContent={formContent}
        handleSubmit={e => handleSubmit(e)}
        handleTitleInput={e => setFormTitle(e.target.value)}
        handleContentInput={e => setFormContent(e.target.value)}
      />
    </View>
  )
}

Index.config = {
  navigationBarTitleText: '首页',
}
Copy the code

Pay attention to

Since we changed Index from a class component to a function component, mount Config directly onto Index after the Index component is defined.

You can always open the emulator and try the refactoring to see if it works exactly as you did in the previous step. In the second installment, we’ll take multi-page jumps a step further and update our interface with the Taro UI component library.

Want to learn more exciting practical skills tutorial? Come and visit the Tooquine community.

The source code for this article is available on Github. If you think we did a good job, please give ❤️ a thumbs up +Github repository + star ❤️