曦远 (DealiAxy)一份私人的文字刊物

StarBlog
返回文章列表折页手记阅读全文
Web前端

Notes on React + TypeScript + Taro Frontend Development

Preface The project has reached a stopping point, so I'll jot some notes down first. This article records, from a frontend beginner's perspective, my development experience with React, TypeScript, and Taro, as well as the problems I ran into and how I solved them. I had always said I wanted to learn React (this blog post: Code Gives Me a Headache: First Steps with React). This project needed an H5 frontend plus a mini program, so I finally got to use React~ I had heard of JD's Taro as a React development framework before, so I went with that and started coding right away. About React Pretty good. It feels much freer than Vue's template syntax

曦远 (DealiAxy)约 31 分钟
目录17 个章节

Preface

The project has reached a stopping point, so I'll jot some notes down first. This article records, from a frontend beginner's perspective, my development experience with React, TypeScript, and Taro, as well as the problems I ran into and how I solved them.

I had always said I wanted to learn React (this blog post: Code Gives Me a Headache: First Steps with React). This project needed an H5 frontend plus a mini program, so I finally got to use React~

I had heard of JD's Taro as a React development framework before, so I went with that and started coding right away.

About React

Pretty good. It feels much freer than Vue's template syntax. The examples in the Taro docs are all class components, but from the start my "frontend mentor" recommended function components. Now I use function components for everything—this is how React should be written. So good~

Because I had written Flutter for a while before, React felt familiar to me. At least I could pick up the declarative UI style seamlessly.

But I feel React's ecosystem is too large and updates too fast—it's a bit fragmented. A lot of third-party libraries' official docs can't keep up with the update pace (a little criticism for MobX here—it really led people astray)

Speaking of which, at first I read some Zhihu influencer's book on React and Redux. Probably because I'm too much of a noob, it didn't feel very easy to understand. I guess being a technically strong expert doesn't necessarily mean you're also good at teaching~

References

About TypeScript

First time using TypeScript, but as someone who writes backends in C# day to day, it felt familiar all over again~

Anyway, it's ten thousand times better than JS, that's for sure. Type hints are just amazing.

I haven't used it very deeply yet. If I run into more related stuff later, I'll write another blog post about it.

References

Impressions of the Taro Framework

The framework is a good framework, but the documentation doesn't feel very complete yet. Some parts aren't written very clearly. A lot of the docs feel like they were copied from the WeChat Mini Program docs, which isn't very friendly for people who haven't developed WeChat Mini Programs and haven't read the official WeChat docs. (You still need to read the official docs though—otherwise you won't be able to look up a lot of the problems you run into.)

Taro officially provides a UI library called TaroUI. It's usable, just that updates are too slow. Its GitHub project page shows the last update was still June of last year (2021)—it hasn't been touched in nearly a year. The latest stable version is still 2.x, while the Taro framework has already been updated to version 3.

Since I was using Taro 3.x, I had no choice but to grit my teeth and use the TaroUI 3 beta, which led to some weird issues. What a headache.

Besides the TaroUI UI library, Taro also officially provides another library called NutUI, but it's Vue-based, so I couldn't use it for this project. That library is updated quite diligently—the latest update on GitHub was still 6 hours ago, and it also has 4.2k stars, more than TaroUI's 3.9k. (Looks like React isn't favored at JD~).

I also saw a UI library called Taroify. It looks pretty good and is updated frequently too, but it only has a little over 300 GitHub stars, so I didn't dare use it~ I'll try it next time.

While using it I also referred to these projects/code/docs:

Now on to the main topic. Overall I'll talk about some issues/pitfalls I ran into, and the solutions.

Page Routing Issues

Taro wraps routing-related methods. It wasn't until I finished the project and had time to flip through the WeChat Mini Program docs that I realized this thing is especially similar to mini program routing.

PS: I hate this kind of routing design. I don't know which genius came up with this for mini programs. When there are a lot of pages it's not easy to maintain~

Configure the routes in the app.config.ts file, something like this:

export default defineAppConfig({
  pages: [
    'pages/index/index',
    'pages/info/place',
    'pages/supply/index',
    'pages/user/login',
  ],
  window: {
    backgroundTextStyle: 'light',
    navigationBarBackgroundColor: '#fff',
    navigationBarTitleText: 'WeChat',
    navigationBarTextStyle: 'black'
  },
})

Then wherever you need to navigate, just use Taro.navigateTo({url: 'pages/user/login'})

Here's a real pitfall! Taro's hot reload is incomplete. After adding a new page, hot reload doesn't take effect—you have to restart with yarn dev:h5 to see the result. I got burned by this at the start and was howling~

URL Parameter Issues

I mentioned this issue in a previous blog post: Implementing Enterprise WeChat Login in a Django + Taro Decoupled Frontend-Backend Project. Taro itself provides useRouter() for us to read parameters from the URL.

For example, if we add parameters at that navigation above: Taro.navigateTo({url: 'pages/user/login?title=hello'})

Then getting the parameters in the pages/user/login page would be like this

import {useEffect} from "react"
import {useRouter} from "@tarojs/taro"

export default function () {
    const router = useRouter()
    
    useEffect(() => {
        console.log(router.params.title)
    }, [])
}

But when reading the WeChat login server callback parameters, it doesn't work—you just can't get them. You have to take the full URL window.location.href and match it yourself. See this blog post of mine for details: Implementing Enterprise WeChat Login in a Django + Taro Decoupled Frontend-Backend Project

Taro.relaunch Doesn't Clear the URL

This doesn't look like a big issue, but it did cause a small bug: after using WeChat login, logging out doesn't clear the code in the URL. So if you don't close the page and use WeChat login again, that code is still the old one, and it just errors out~

TaroUI Form Bug

To be honest I don't know where this problem comes from

Only one page had this issue. Pressing Enter in the last input box looked like a form submit, but it didn't actually submit, and the page refreshed instead.

I just couldn't figure it out no matter how hard I thought

I had no choice but to add a hidden input at the very end

<AtInput
    name='hide'
    onChange={() => {
    }}
    disabled={true}
    border={false}
    style={{display: 'none'}}/>

Wrapping Network Requests

The Taro framework comes with Taro.request that can be used for requests, but when I used it I kept getting weird CORS errors. Because time was tight early on, I didn't dig into it and just switched to the axios wrapper I had from a previous Vue project. Sure enough, axios is still nicer to use~

(Though if we turn it into a mini program later, we'll probably still need to refactor. I hear mini programs don't support formdata)

Wrapping useState

Thanks to "frontend mentor coppy" for providing this code~

import {useState} from 'react'

export default function useYourState<T extends {}>(state: T): [T, (state: Partial<T>) => void] {
  const [_state, _setState] = useState(state);
  return [
    _state,
    (state: Partial<T>) => {
      _setState((_state) => {
        return {
          ..._state,
          ...state
        };
      });
    }
  ];
}

This way you don't need to add ...state every time you setState.

Before:

import {useState} from 'react'

export const LoginPage = observer(() => {
  const [state, setState] = useState({
    username: '',
    password: '',
  })
  
  setState({
      ...state,
      username: '', password: ''
  })
}

After:

import useYourState from "@/utils/coppy_state";

export const LoginPage = observer(() => {
  const [state, setState] = useYourState({
    username: '',
    password: '',
  })
  
  setState({
      username: '', password: ''
  })
}

Productivity went up~

Global State Management

I didn't go with the famous Redux, and instead used the relatively simpler MobX.

But the example docs I found weren't great (for example, the official Chinese docs: https://cn.mobx.js.org/)

In the end I still sought help from my "frontend mentor" and got it working.

Pitfalls:

  • Stores can no longer use decorators now; use this makeAutoObservable
  • No need for a global provider
  • The Taro official site and examples are arguably the biggest trap in history. Don't get fooled. URL: https://taro-docs.jd.com/taro/docs/mobx/
  • Please use the latest versions of MobX and mobx-react-lite. Don't use the 4.8 version on the Taro official site—it's too old and useless

Code

No need to wrap with a global provider anymore. Just use a global variable. Of course you can also use React Context.

Store definition

import {makeAutoObservable} from "mobx";
import {User} from "@/models/user";
import * as auth from '@/utils/auth'

export class UserStore {
  isLogin = false
  user: User | null = null
  token = ''

  constructor() {
    makeAutoObservable(this)
  }

  login(user: User, token: string) {
    this.user = user
    this.token = token
    this.isLogin = true

    // 保存登录数据到本地
    auth.login(token, user.username)
  }

  logout() {
    this.isLogin = false
    this.user = null
    this.token = ''

    auth.logout()
  }
}

export const myUserStore = new UserStore()

Component usage

import {View} from "@tarojs/components"
import {AtButton} from "taro-ui";
import {observer} from "mobx-react-lite";
import {myUserStore} from "@/store/user";
import Taro from "@tarojs/taro";
import {useEffect} from "react";

export const UserPage = observer(() => {
  useEffect(() => {
    if (!myUserStore.isLogin) {
      Taro.redirectTo({url: '/pages/user/login'})
    }
  }, [])

  return (
    <View className='py-3 px-2'>
      <View className='at-article__h1'>用户中心</View>

      <View className='at-article__h3 mt-1'>用户名:{myUserStore.user?.first_name}</View>

      <AtButton className='mt-3' onClick={logout}>退出登录</AtButton>
    </View>
  )

  function logout() {
    myUserStore.logout()
    Taro.reLaunch({url: '/pages/index/index'})
  }
})

export default UserPage

References

Deserializing JSON to a Class

Use this library: https://github.com/typestack/class-transformer

Model definition. This definition can be generated from JSON. There are many online tools, for example: https://apihelper.jccore.cn/jsontool

export class User {
  username: string
  first_name: string
  last_name: string
  email: string
  date_joined: string
}

Note that the docs on the project homepage are also outdated. The plainToClass method is deprecated; you need to use this method: plainToInstance

import {plainToInstance} from "class-transformer";

const user = plainToInstance(User, res.data.user)
myUserStore.login(user, res.data.token)

_EOF_

Notes on React + TypeScript + Taro Frontend Development

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…