directory

  • Write a React Hooks + KOA-style Web framework
  • What is the development experience of scheduling useEffect and memo for a session?

preface

Recently, I have been planning to write a Web framework for my own use, but I have been troubled by the interdependence between plug-ins, so I temporarily stopped the development recently.

But some interesting ideas have popped up recently from learning React: could nodeJS code be written in the Style of React hooks?

First, design API style

Main application code assumption

import { Server } from 'http'
import { isNullOrUndefined } from 'util'

import { use, route, redirect, urlFor, listen } from '@zhengxs/koa-hooks'

import { useSession } from './session'

route('product.detail'.'/api/product/:id', (ctx) => {
  ctx.status = 200
  ctx.type = 'application/json'
  ctx.body = ctx.params
})

route('/api/user/info', (ctx) => {
  const sess = useSession()
  const userId = sess.userId

  // The user is redirected to the login page without logging in
  if (isNullOrUndefined(userId)) {
    return redirect(urlFor('login'))
  }

  ctx.status = 200
  ctx.type = 'application/json'
  ctx.body = {
    userId: userId,
    nickname: 'Joe',
  }
})

route('POST'.'/login', (ctx) => {
  const sess = useSession()

  // Write the user ID
  sess.userId = 1

  ctx.status = 200
  ctx.type = 'application/json'
  ctx.body = { code: 200.message: 'ok' }
})

route('login'.'/login', (ctx) => {
  ctx.status = 200
  ctx.type = 'text/plan'
  ctx.body = 'This is login page'
})

route('/view', (ctx) => {
  const sess = useSession()

  sess.view = sess.view || 0

  ctx.status = 200
  ctx.type = 'application/json'
  ctx.body = { view: sess.view++ }
})

use((ctx) = > {
  ctx.body = `hello,world`
})

listen(8080.function onReady(this: Server) {
  console.log('Liston http://127.0.0.1:8080')})Copy the code

The main application code doesn’t seem to be anything special at first, just the app creation step has been removed, but wait, let’s see what’s special about the custom hooks design.

Custom hooks design

For example, in 🌰, where the server receives a private message from the user and forwards it to the recipient, the process looks something like this

The code logic looks something like this

async saveMsgToDB(msgData, next){
    // Get the current request context
    const db = useContext('db')
    // Database operation
    
    // Stream the processed message to the next task
    return next(msg)
}

// Point-to-point message sending
const sendToUser = series(
  // Save data
  saveMsgToDB,
  // Message notification
  parallel(
    // Send messages to the client
    sendMsgWithSocketIo,
    // Convert the message into notification data and push it to wechat
    series(msgToNotice, pushNoticeToWeChat)
    // Convert the message into notification format data and push it to nail
    series(msgToNotice, pushNoticeToDingTalk)
  )
)
Copy the code

From the above we can see that custom hooks are unique in that:

  1. Each is a pure function and is not bound by any framework constraints. If context is required, use it explicitlyuseContext.
  2. Since they are pure functions, we can wrap another layer around them to avoid redundant code, such as notifications that can be independent functions, or they can be wrapped into a task function.

The following functions, for example, can be used as generic functions, as long as the data format is agreed, because you need context and so on, you can get it internally using useContext.

// Push to pin
pushNoticeToWeChat

// Push to pin
pushNoticeToDingTalk

// Send messages to the client
sendMsgWithSocketIo
Copy the code

Two, to achieve the function

Difficulty: How to solve the problem of consistent context in request processing

There were early bumps in the road in this no-nonsense operation

Web services are stateless and receive multiple client requests, with many asynchronous operations in between, making it difficult to ensure context consistency for each function being executed.

After several failed hacks, I encountered Async Hooks added to Node 8.

The significance of this module is that we can obtain the unique ID generated by node for this function, i.e. Id ++ for each call, and guarantee that the id of the entry function obtained by subsequent functions will not change during the process

We can see that the subsequent function gets a triggerAsyncId of 6, and triggerAsyncId accumulates every time the entry is called again, but the stack of triggerAsyncId that the subsequent function gets is the same.

So we can do context switch based on this, core code:

A new triggerAsyncId is generated each time the requestListener method is called, but the triggerAsyncId obtained by the intermediate function is the same. This solves the problem of context switching when multiple contexts are present

The last

In Python, framework contexts such as Flask can be retrieved globally, which has the advantage of not being particularly constrained by the framework.

The downside is that it’s too flexible. I still write vue code because React is so flexible that I can write things. Every time I look at the code, I feel like I can change something.

The first version of the code has now been released to Github and NPM. The first Async Hooks cannot be fixed, but they are still interesting to learn.

  • Making portal
  • NPM portal