Welcome to continue reading the Taro Small program Development large-scale Practical series, previously on:
- Familiar React, familiar Hooks: We implemented a very simple prototype for adding posts using React and Hooks
- Multi-page hops and the Taro UI Component Library: We implemented multi-page hops with the built-in Taro routing function, and upgraded the application interface with the Taro UI component library
- Realize wechat and Alipay multi-terminal login: realize wechat, Alipay and ordinary login and logout login
- Redux: Use the Hooks version of Redux to refactor your application’s state management
- Large Application State Management using Redux from Hooks (Part 1)Redux is implemented using the Hooks version
user
State management refactoring of logic - Large application State Management with Redux from Hooks (Part 2)Redux is implemented using the Hooks version
post
State management refactoring of logic
After if you come to here, you will find our content is pure front end (small end) logic, a complete online small applications should also have the back-end, in this article, we will use the WeChat applet cloud as our background, then we will introduce the story – saga to help redux elegant processing asynchronous processes, The final results of this paper are as follows:
If you’re not familiar with Redux, we recommend reading our Redux Tutorial series:
- Redux Redux Redux Redux Redux Redux Redux Redux Redux Redux
- Redux: Strike while the iron is hot and completely refactor
- Redux Education Package Association (3) : To perform their respective duties and regain the original purpose
If you want to start directly from this step, run the following command:
git clone -b miniprogram-start https://github.com/tuture-dev/ultra-club.git
cd ultra-club
Copy the code
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 ❤️
In order to make persistent storage of data and efficient query, we need to store the data in the database. In order to realize the convenient development experience of the small program side, a large number of small program Serverless services rise, and wechat small program cloud is born for the rapid development of wechat small program. In this article, we will use wechat applet cloud as our back end and explain how to introduce and implement Redux asynchronous workflow to achieve state management of applet side access to the applet cloud.
Wechat small program cloud at first taste
In the previous code, we use the data stored in the Storage to complete data persistence inside, so that we can solve the problem of small data Storage and query, once the data quantity big, the query and store to need to rely on specialized database to solve, we can generally addressed by way of the back-end and database construction, However, when small programs are becoming more and more popular at the same time, a model called Serverless is proposed and gradually popular. In a popular sense, it is summarized as “no back end”, that is, the back end is handed over to cloud service manufacturers (Ali Cloud, Tencent Cloud, Jingdong Cloud, etc.). Developers only need to focus on front-end logic and fast delivery of functions.
The common applets Serverless service contains three main functions:
- Database: Data is usually stored in JSON data format and can be stored in the cloud database.
- Storage: Supports storage of user-generated content such as texts and images. You can obtain resource links for use.
- Cloud functions: you can use Node.js for development, write the corresponding back-end logic, and upload the written code to the cloud, and then use API in front of the small program to call.
About the detailed description of the small program Serverless, here is a recommended article, interested students can understand in detail: What is the small program Serverless?
In this section, we use wechat small program cloud as our “back end”, wechat small program cloud and small program account are bound together, a small program account can open a small program cloud space, next, we will explain in detail how to open the small program cloud.
Open the small program cloud
- First of all, make sure you register the wechat public platform account: registered address.
- After login, go to the menu bar Development > Development Settings
AppID
It should be an 18-bit string. - useWechat developer toolsOpen up our
ultra-club
Project folder, and then choose Settings > Project Settings from the menu bar of wechat Developer Tools to open the Settings bar:
4. Find the basic information of the setting bar and change it to the AppID as follows:
5. When the AppID is set, the “Cloud Development” button in our developer tools should become clickable. Find the “Cloud Development” button in the upper left corner and click it, similar to the picture below:
4. Click “Cloud development” button and the confirmation box will pop up. Click “Agree” and you will enter the small program cloud development console:
The first thing we’ll see is the Cloud Development Console’s “Operations Analytics” interface, which is used to visualize the use of various cloud development resources, something we won’t cover in this tutorial. Let’s focus on the part highlighted in red:
- The number 1 is our cloud database, which is a JSON database, which stores the data we need during development.
- Number 2 is storage, which means we can upload some text, pictures, audio/video, and then return us links to access those resources.
- Number 3 is the cloud function, which is where we can manage some back-end Node.js logic we wrote, which runs in the cloud, and which we can call from the applets through the API.
- No. 4 is the identifier that represents our cloud environment this time, which can be used to mark the cloud environment that is called at this time when the API of the small program side calls cloud development resources.
In this tutorial, we will use the database and cloud functions mentioned above.
Create a database table
Introduce the interface of the small program cloud, we immediately start to practice, to create the database table we need, because our front-end logic is mainly divided into user and POST two types of logic, so we create two tables in the database:
Here we specifically to explain the meaning of the database operation interface:
- As you can see, click the second button in the upper left corner of the Cloud Development Console, and then click the “+” button marked red 1 to create two collections
user
和post
, so we have created our database table. - A number of 2 means we can select a collection and right-click to delete it.
- The number 3 indicates that we can add records to a collection, and since it’s a JSON database, each record in the collection can be different.
- The serial number 4 indicates that we can select a record and right click to delete it
- The ordinal number 5 indicates that we can add fields to individual records
- The number 6 indicates that a single record can be selected for deletion/modification
- The serial number 7 indicates that we can query a particular record in the collection
createpost
record
Here we add a default POST record, which represents the default data of our previous applet. This data record contains information about post:
_id
: Unique identifier of the datatitle
Article titlecontent
: Content of the articleuser
: The user who published this article, we save the full information of the user directly for convenience, general best practice is to save this user_id
Properties, and then in the querypost
Fetch the user’s_id
Property, and look it upuser
Get complete information about the user.updatedAt
: Indicates the last update time of this recordcreatedAt
: Indicates the creation time of the record
createuser
record
As mentioned above, we have saved the author’s information in this post record, so of course we need to create a new user set with the following information:
As you can see, we have added a user record with the following fields:
_id
: The user is inuser
A unique identifier in a collectionavatar
: Profile picture address of the usernickName
: the nickname for this user, which we will use to log increatedAt
: The time when the record was createdupdatedAt
: Time when this record was last updated
Initialize the applet cloud environment on the applet side
After opening the small program cloud, we also need to initialize the small program cloud environment in the small program front-end code, so as to call the API of the small program in the small program front.
Open the SRC /index/index.jsx file and add the following code:
import Taro, { useEffect } from '@tarojs/taro'
/ /... The rest of the code is consistent
export default function Index() {
/ /... The rest of the code is consistent
useEffect((a)= > {
const WeappEnv = Taro.getEnv() === Taro.ENV_TYPE.WEAPP
if (WeappEnv) {
Taro.cloud.init()
}
/ /... The rest of the code is consistent
return (
<View className="index">.</View>)}Copy the code
As can be seen, we have added the acquisition and judgment of the Micro-program environment. When the current environment is the micro-program environment, we need to call taron.cloud.init () to initialize the micro-program cloud environment
summary
So far, we have explained how to open the applet cloud, and then the applet cloud console interface. At the same time, we have explained the database function interface that we will use, where we have created two tables (sets) for our application: POST and User, and initialized a record each.
Ok, now that we have the applets cloud ready, we are ready to plug it into our application, but before that, since we are going to plug into the applets cloud, we are going to have to make asynchronous requests, which requires us to understand the asynchronous processing flow of Redux. In the next section, We will use the Redux-Saga middleware to simplify the process of redux handling asynchrony.
Redux asynchronous workflow resolution
Let’s take a look at Redux’s data flow diagram:
The grey path in the figure above is the Redux data flow graph that we have been using. It is the Redux synchronous data flow graph:
view
中dispatch(syncAction)
A synchronous action to updatestore
The data in thereducer
Update in response to actionstore
stateconnect
Pass the updated status toview
view
Receive new data for re-rendering
Pay attention to
For those of you who are not familiar with Redux, take a look at the Tuq Redux Tutorial series.
Now we are going to go small program cloud initiate the request, the request is an asynchronous request, it is not immediately get a response, so we need an intermediate state (in this case we use the Saga) deal with the asynchronous request and receive data back and forth, and then execute and synchronous request before a similar path, the green part of the above for us + remaining gray part, So the asynchronous workflow looks like this:
view
中dispatch(asyncAction)
An asynchronous action to get the data from the back end (in this case, the applets cloud)saga
Process the asynchronous action and wait for the data responsesaga
Get the data for the response,dispatch(syncAction)
A synchronized action to update the store statereducer
Update in response to actionstore
stateconnect
Pass the updated status toview
view
Receive new data for re-rendering
Pay attention to
The Tuq community will publish a tutorial on the Redux asynchronous workflow in the future. This tutorial will not go into the mechanics of the entire asynchronous workflow, but how to integrate the asynchronous workflow. Stay tuned ✌️~
Actual Redux asynchronous workflow
The installation
We used the redux-Saga middleware to take over the processing of the asynchronous request part of the Redux asynchronous workflow by first installing the Redux-Saga package in the project root directory:
$ npm install redux-saga
Copy the code
After installation, our package.json should look like this:
{ "dependencies": { ... Redux-saga: "^1.1.3", "taro-ui": "^2.2.4"},}Copy the code
Redux-saga is redux’s middleware that handles asynchronous processes. What is Saga? Saga is defined as a “Long Lived Transaction” (LLT). He was the brainchild of HECTOR Garcia-Molina, a Professor at Princeton University, in a 1987 paper on distributed databases.
A saga is officially likened to a single thread in an application that handles its own side effects, which in JavaScript are asynchronous network requests, local reads of localStorage/ cookies, and other external operations.
configurationredux-saga
The middleware
After installing redux-saga, we need to configure redux-saga to use it. Open the SRC /store/index.js file and make the following changes:
import { createStore, applyMiddleware } from 'redux'
import { createLogger } from 'redux-logger'
import createSagaMiddleware from 'redux-saga'
import rootReducer from '.. /reducers'
import rootSaga from '.. /sagas'
const sagaMiddleware = createSagaMiddleware()
const middlewares = [sagaMiddleware, createLogger()]
export default function configStore() {
conststore = createStore(rootReducer, applyMiddleware(... middlewares)) sagaMiddleware.run(rootSaga)return store
}
Copy the code
As you can see, our file above has made the following four changes:
- So first we derived it
createSagaMiddleware
- And then we go from
src/store/sagas
One is exported under the folderrootSaga
It combines all of themsaga
Files, this is kind of a combinationreducer
的combineReducers
, we will write these in subsequent stepssagas
. - And then we call
createSagaMiddleware
generatesagaMiddleware
Middleware and place it inmiddleware
Array, so that Redux registers the middleware, and when responding to an asynchronous action,sagaMiddleware
Will come in and hand it over to ussaga
Delta function. - Finally, in
createStore
Function, when creatingstore
After that, we callsagaMiddleware.run(rootSaga)
To take all thesagas
Run and start listening for and responding to asynchronous actions.
View initiates an asynchronous request
With redux-Saga middleware configured and Sagas running, we can dispatch asynchronous actions in React.
Let’s follow the order of refactoring, first to get the login of the asynchronous data stream processing, open the SRC/components/LoginForm/index. The JSX files, make corresponding modifications to the content as follows:
import Taro, { useState } from '@tarojs/taro'
import { View, Form } from '@tarojs/components'
import { AtButton, AtImagePicker } from 'taro-ui'
import { useDispatch } from '@tarojs/redux'
import { LOGIN } from '.. /.. /constants'
import './index.scss'
export default function LoginForm(props) {
// Other logic unchanged...
async function handleSubmit(e) {
// Other logic unchanged...
// Cache in storage
const userInfo = { avatar: files[0].url, nickName: formNickName }
// Clear the form state
setFiles([])
setFormNickName(' ')
// The backend initiates a login request
dispatch({ type: LOGIN, payload: { userInfo: userInfo } })
}
return (
// The returned component...)}Copy the code
As you can see, we made the following three changes to the above code:
- We will set the user login information before
SET_LOGIN_INFO
And set the login box pop-up layerSET_IS_OPENED
Replaced with aLOGIN
Constant, which means that we need to initiate a login request to the small program cloud first, and then get the login data and then set the login information and close the pop-up layer of the login box (in fact, here can also directly close the pop-up layer, a little error… . - We then delete the previous actions of setting login information and turning off the pop-up layer of the login box.
- And finally we are going to
dispatch
aaction.type
为LOGIN
Action with the information we need to log inuserInfo
.
Add Action constant
We used the LOGIN constant in the previous step, open SRC /constants/user.js and add the LOGIN constant to it:
export const SET_IS_OPENED = 'MODIFY_IS_OPENED'
export const SET_LOGIN_INFO = 'SET_LOGIN_INFO'
export const LOGIN = 'LOGIN'
Copy the code
Saga handles asynchronous requests
Saga handles asynchronous requests in a number of different ways, depending on the project, but we have used the official best practices:
- WatcherSaga listens for asynchronous actions
- HandlerSaga handles asynchronous actions
dispatch
For synchronous actions, update the starting status of asynchronous actionsdispatch
Update the success/failure status of synchronous actions
Using recent practice, the previous Redux data flow diagram looks like this:
Now that we’ve covered the best practices of Redux-Saga for handling asynchronous actions, we’ll apply the best practices to writing saga files for handling asynchronous actions.
Multiple asynchronous requests may be involved in our application, so Redux-Saga recommends that the best practice is to create a separate SAGAS folder to hold all the SAGAS files that handle asynchronous requests, as well as any ancillary files that may be needed.
In the previous step, we made the LOGIN asynchronous LOGIN request from the View. Now we need to write the saga file that handles this LOGIN request, create the sagas folder in the SRC folder, and create user.js in it, and write the following:
import Taro from '@tarojs/taro'
import { call, put, take, fork } from 'redux-saga/effects'
import { userApi } from '.. /api'
import {
SET_LOGIN_INFO,
LOGIN_SUCCESS,
LOGIN,
LOGIN_ERROR,
SET_IS_OPENED,
} from '.. /constants'
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * the login logic start * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
function* login(userInfo) {
try {
const user = yield call(userApi.login, userInfo)
// Cache user information locally
yield Taro.setStorage({ key: 'userInfo'.data: user })
// The following three steps can be combined into one step, but they are broken up into separate units for clarity
// Initiate a successful login action
yield put({ type: LOGIN_SUCCESS })
// Close the login pop-up layer
yield put({ type: SET_IS_OPENED, payload: { isOpened: false}})// Update Redux store data
const { nickName, avatar, _id } = user
yield put({
type: SET_LOGIN_INFO,
payload: { nickName, avatar, userId: _id },
})
// A message indicating successful login is displayed
Taro.atMessage({ type: 'success'.message: 'Congratulations! Login successful! '})}catch (err) {
console.log('login ERR: ', err)
// Failed to log in. Failed to initiate action
yield put({ type: LOGIN_ERROR })
// Login failed
Taro.atMessage({ type: 'error'.message: 'I'm sorry! Login failed! '}}})function* watchLogin() {
while (true) {
const { payload } = yield take(LOGIN)
console.log('payload', payload)
yield fork(login, payload.userInfo)
}
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * the login logic end * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
export { watchLogin }
Copy the code
As you can see, the above changes mainly create watcherSaga and handlerSaga.
createwatcherSaga
- We created the logged in
watcherSaga
:watchLogin
It is used to listen inaction.type
为LOGIN
Action, and when listeningLOGIN
After the action, get the necessary ones from the actionuserInfo
Array, and then activatehandlerSaga
:login
To handle the corresponding logon logic. - Here,
watcherSaga
:watchLogin
Is a generator function that contains awhile
Infinite loop, which means continuous listening insideLOGIN
The action. - Inside the loop, we use
redux-saga
To provide theeffects helper
Function:take
It is used to listenLOGIN
Action to get the data carried in the action. - And then we used another one
effects helper
Function:fork
, which represents non-blocking executionhandlerSaga
:login
And willpayload.userInfo
Pass as a parameterlogin
.
createhandlerSaga
- We created the logged in
handlerSaga
:login
, which handles login logic. login
Is also a generator function, and inside it is atry/catch
Statement to handle possible error cases in login requests.- in
try
Statement, the first is usedredux-saga
Provided to useffects helper
Function:call
To call the login API:userApi.login
And put theuserInfo
Pass it to the API as a parameter.- And then if we login successfully, we will login successfully
user
The cache tostorage
The inside. - Next, we use
redux-saga
To provide theeffects helpers
Function:put
.put
Similar to beforeview
In thedispatch
Operation, comedispatch
There are three actions:LOGIN_SUCCESS
.SET_IS_OPENED
.SET_LOGIN_INFO
To update the login status, close the login box, and set the login information to the Redux Store. - Finally, we used the message box provided by Taro UI to display a
success
The message.
- And then if we login successfully, we will login successfully
- If the login fails, we use
put
To launch aLOGIN_ERROR
Action to update the failed login information to the Redux Store, and then use the message box provided by Taro UI to display oneerror
The message.
Pay attention to
For those unfamiliar with generator functions, take a look at this document: Iterators and Generators.
Some extra work
To create watcherSaga and handlerSaga, we also imported the userApi, which we will create later.
We also import the action constants we need to use:
SET_LOGIN_INFO
: Sets the login informationLOGIN_SUCCESS
: Updates the login success informationLOGIN
: Listens for login actionsLOGIN_ERROR
: Updates the login failure informationSET_IS_OPENED
: Enables or disables the login dialog box
We also imported the necessary functions from the Redux-Saga/Effects package:
call
In:saga
Function to call other asynchronous/synchronous functions to get the resultput
Similar to:dispatch
forsaga
Initiate action in the functiontake
In:saga
Function to listen on the action and get the data carried by the corresponding actionfork
In:saga
A non-blocking call to a functionhandlerSaga
That is, after the call, no subsequent execution logic is blocked.
Finally, we exported watchLogin.
createsaga
Central dispatch file
In the previous step, we exported watchLogin, which is similar to a single reducer function in reducers. We also need to combine all watcherSaga as combineReducers combined reducer.
Create an index.js file in the SRC /sagas folder and write the following in it:
import { fork, all } from 'redux-saga/effects'
import { watchLogin } from './user'
export default function* rootSaga() {
yield all([
fork(watchLogin)
])
}
Copy the code
As you can see, there are three major changes to the file above:
- We learn from
redux-saga/effects
Derived byeffects helper
functionfork
和all
. - And then we go from
user.js
Saga importedwatchLogin
。 - And finally we derived one
rootSaga
, which is the center for scheduling all sagAS functions through theall
The function passes in an array, andfork
Non-blocking executionwatchLogin
To start listening and distributing asynchronous actions, once listeningLOGIN
Action is activatedwatchLogin
The processing logic inside.
Pay attention to
The only array received by all is fork(watchLogin). When the asynchronous logic of POST is added later, multiple forks (watcherSaga) will be added to the array.
Add action constant
Since we used some undefined constants in the User saga file from the previous step, let’s define them immediately, open SRC /constants/user.js and add the corresponding constants as follows:
export const SET_IS_OPENED = 'MODIFY_IS_OPENED'
export const SET_LOGIN_INFO = 'SET_LOGIN_INFO'
export const LOGIN = 'LOGIN'
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'
export const LOGIN_ERROR = 'LOGIN_ERROR'
export const LOGIN_NORMAL = 'LOGIN_NORMAL'
Copy the code
As you can see, in addition to the constant we used in “Saga handling asynchronous requests”, there is a LOGIN_NORMAL constant, which is mainly used to set the default state of the login state.
Implement the request Login API
In the previous User Saga file, we used the userApi, which encapsulates the logic for making requests to the back end (in this case, the applet cloud). Let’s implement it now.
We put all the API files in the API folder, which is convenient for us to maintain the code in the future. We created the API folder in the SRC folder, added the user.js file to it, and wrote the following contents in the file:
import Taro from '@tarojs/taro'
async function login(userInfo) {
const isWeapp = Taro.getEnv() === Taro.ENV_TYPE.WEAPP
const isAlipay = Taro.getEnv() === Taro.ENV_TYPE.ALIPAY
// Use the miniprogram cloud function for wechat miniprograms, and the rest use the miniprogram RESTful API
try {
if (isWeapp) {
const { result } = await Taro.cloud.callFunction({
name: 'login'.data: {
userInfo,
},
})
return result.user
}
} catch (err) {
console.error('login ERR: ', err)
}
}
const userApi = {
login,
}
export default userApi
Copy the code
In the above code, we define the login function, which is an async function used to process asynchronous logic. In the login function, we evaluate the current environment and perform login operations only under the conditions of isappellate apps. We’ll address this in the next section using LeanCloud Serverless.
The logon logic is a try/catch statement that catches possible request errors. In the try code block, We use the cloud function API taro.cloud. callFunction of the wechat applet cloud provided by Taro to conveniently initiate cloud function call requests to the applet cloud. Its call body is an object similar to the following structure:
{
name: ' '.// The name of the cloud function to call
data: {} // The data to be passed to the cloud function
}
Copy the code
Here we call a login cloud function, which we will implement in the next section, and pass userInfo as a parameter to the cloud function to use the user information in the cloud function to register the user and save it to the database.
prompt
To learn more about the wechat applets cloud function, you can refer to the wechat applets cloud function documentation: Document address
If the call is successful, we can receive the return value, which is used to return data from the back end. Here we use the deconstruction method, taking the Result object from the return body, and then retrieving the User object as the return value of the Login API function.
If the call fails, an error is printed.
Finally, we define a userApi object to hold all functions associated with the user logic, add the Login API attribute and export it. This allows you to import the userApi in the User Saga function and then call the login API to handle the login logic as userapi.login.
Create the default API export file
We created SRC/API /user.js file, we need to create a unified default file for exporting all API files, convenient for unified distribution of all API files, create index.js file in SRC/API folder, and write the following contents in it:
import userApi from './user'
export { userApi }
Copy the code
As you can see, we exported the userApi from user.js by default and added it as a property of the object exported by Export.
Configure the cloud function development environment
We called the login cloud function in the previous section using the cloud function API provided by Taro. Now we will implement the cloud function.
In the wechat applet document, we are required to create a folder to store cloud functions under the root directory of the project, and then specify the value of cloudfunctionRoot field in project.config.json as this directory, so that the applet developer tool can identify this directory as the directory to store cloud functions. And do special mark processing.
We created a functions folder at the root of the project, which is the same as the SRC folder:
. ├ ─ ─ LICENSE ├ ─ ─ the README. Md ├ ─ ─ the config ├ ─ ─ dist ├ ─ ─functions├ ─ ─ node_modules ├ ─ ─ package. Json ├ ─ ─ project. Config. The json ├ ─ ─ the SRC ├ ─ ─ tuture - assets ├ ─ ─ tuture - build ├ ─ ─ tuture. Yml └ ─ ─ yarn.lockCopy the code
Json in the root directory and set it to ‘functions/’ as follows:
{
"miniprogramRoot": "dist/"."projectname": "ultra-club"."description": ""."appid": ""."cloudfunctionRoot": "functions/"."setting": {
"urlCheck": true."es6": false."postcss": false."minified": false
},
"compileType": "miniprogram"."simulatorType": "wechat"."simulatorPluginLibVersion": {},
"cloudfunctionTemplateRoot": "cloudfunctionTemplate"."condition": {}}Copy the code
As you can see, when we create the folder above and set project.config.json, our applet developer tool will look something like this:
We create the folder functions provides one more additional cloud icon, and a folder named transformed from functions provides functions provides | ultra – club, vertical bar on the right is a small program that our current environment.
And when we right-click on the Functions folder in the Applets developer tools, a menu pops up that allows us to perform cloud function-related actions:
We can see that there are many operations. Here we mainly use the following operations:
- Create a new Node.js cloud function
- Enable local cloud function debugging
Pay attention to
Other operations will be encountered when you need to write more complex business logic after you complete the development process of the small program cloud. For details, please refer to the document of the small program cloud: Document address.
Pay attention to
You must first open the applets cloud development environment to use cloud functions. Specific steps can refer to our explanation in the section of “Opening the small program cloud”.
Create the Login cloud function
We explained the configuration of wechat applet cloud functions, and finally arrived at the stage of creating cloud functions. We right-click functions folder in the applet developer tool, and then select New Node.js cloud function, enter login, and press Enter to create. We’ll see that the applets developer tool automatically creates the following code file for us:
As you can see, a cloud function is a separate Node.js module that handles a class of logic.
Let’s look at the package.json file as follows:
{
"name": "login"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": ""."license": "ISC"."dependencies": {
"wx-server-sdk": "latest"}}Copy the code
As you can see, when adding the cloud function, the applet developer tool adds a wX-Server-SDK dependency by default, and we need to use its built-in API in the cloud function to operate the applet cloud.
To make the Node.js cloud function/project run, we need to install the dependencies, go to the functions/login directory and run NPM install to install the dependencies.
Learn about the default generated cloud functions
Once the cloud functions have been created and the dependencies installed, we can immediately unmask the cloud functions by opening functions/login/index.js and see the following code:
// Cloud function entry file
const cloud = require('wx-server-sdk')
cloud.init()
// Cloud function entry function
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
}
}
Copy the code
As you can see, the default generated code does the following:
- The import
wx-server-sdk
Package and name itcloud
All the methods we need to manipulate the applet cloud are bound tocloud
On the object. - Then call
cloud.init()
To initialize the cloud development environment for cloud functions, which we will implement laterlogin
Logical time to set the environment. - Finally, there is the entry function of the cloud function, which defaults to
main
The function, as the derived function, is aasync
Function, we can handle asynchronous logic synchronously inside the function. As you can see, this function takes two arguments:event
和context
.event
Refers to the event that triggers the cloud function. When the applet side calls the cloud function, the event is the parameter passed in when the applet side calls the cloud function, plus the parameter of the applet user automatically injected by the back endopenid
And small programappid
.context
Object contains the call information and the running state of the call here, which you can use to see how the service is running. The default generated function internal code is mainly to obtain the wechat context information at this time, and then withevent
Objects return together, so that when we’re on the applet sideTaro.cloud.callFunction
The return value from calling this function is containing wechat context information andevent
The object.
Write the Login cloud function
Now that we know the specific logic of the cloud function, we immediately implement our specific login logic in the cloud function, open functions/login/index.js, and make the corresponding modifications to the code as follows:
// Cloud function entry file
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV,
})
const db = cloud.database()
// Cloud function entry function
exports.main = async (event, context) => {
const { userInfo } = event
console.log('event', event)
try {
const { data } = await db
.collection('user')
.where({
nickName: userInfo.nickName,
})
.get()
if (data.length > 0) {
return {
user: data[0].}}else {
const { _id } = await db.collection('user').add({
data: {
...userInfo,
createdAt: db.serverDate(),
updatedAt: db.serverDate(),
},
})
const user = await db.collection('user').doc(_id)
return {
user,
}
}
} catch (err) {
console.error(`login ERR: ${err}`)}}Copy the code
As you can see, the code changes above are mainly as follows:
- First we give
cloud.init()
Passed in the environment parameters, we use the built-incloud.DYNAMIC_CURRENT_ENV
, means automatically set to the current cloud environment, that is, right click in the applets developer toolsfunctions
Folder to select the environment. - And then we go through
cloud.database()
A data instance is generateddb
For easy operation of the cloud database in the function body. - Then there is
main
The body of the function, we start with theevent
Object is taken from the call in the appletTaro.cloud.callFunction
To get theuserInfo
The data. - And then, there’s a guy who takes the data
try/catch
Statement block, used to catch errors, intry
Statement block, we usedb
Query operation:db.collection('user').where().get()
, indicating querywhere
Conditions of theuser
Table data, which checks out should be an array if there is no satisfywhere
Conditional, then is an empty array if there is one that satisfieswhere
Conditional, then return oneuser
The array. - Next, we determine whether the queried user array is empty. If it is empty, a new user is created. If it is not empty, the first element is returned.
- This is what we use here
db.collection('user').add()
To add auser
Data, and then inadd
Method passed indata
Field to set the initial value for this user, which we use in addition heredb.serverDate()
It is used to record the time of creating the user and updating the user, which is convenient for conditional query. Because adding a record to the database only returns that record_id
So we need an extra operationdb.collection('user').doc()
To get this record, thisdoc
Retrieves the specified record reference and returns the data instead of an array.
Pay attention to
Here about the related operations of the cloud database, you can refer to the wechat small program cloud document, which provides a detailed example: database document.
Reducer reducer for asynchronous actions
When we were dealing with LOGIN earlier, we dispatched the LOGIN action inside the component. In the saga function that handles asynchronous actions, we used PUT to initiate a series of actions that update the LOGIN status in the store. Now let’s implement the reducers in response to these actions. Open SRC /reducers/user.js and modify the corresponding code as follows:
import {
SET_LOGIN_INFO,
SET_IS_OPENED,
LOGIN_SUCCESS,
LOGIN,
LOGIN_ERROR,
LOGIN_NORMAL,
} from '.. /constants/'
const INITIAL_STATE = {
userId: ' '.avatar: ' '.nickName: ' '.isOpened: false.isLogin: false.loginStatus: LOGIN_NORMAL,
}
export default function user(state = INITIAL_STATE, action) {
switch (action.type) {
case SET_IS_OPENED: {
const { isOpened } = action.payload
return { ...state, isOpened }
}
case SET_LOGIN_INFO: {
const { avatar, nickName, userId } = action.payload
return { ...state, nickName, avatar, userId }
}
case LOGIN: {
return { ...state, loginStatus: LOGIN, isLogin: true}}case LOGIN_SUCCESS: {
return { ...state, loginStatus: LOGIN_SUCCESS, isLogin: false}}case LOGIN_ERROR: {
return { ...state, loginStatus: LOGIN_ERROR, isLogin: false}}default:
return state
}
}
Copy the code
Take a look at the code above and see that there are three major changes:
- First we import the necessary action constants
- And then we give
INITIAL_STATE
Several fields have been added:userId
: used to obtain user data later and mark the user’s login statusisLogin
: indicates whether login logic is being executed during login.true
Indicates that login is in progress,false
Indicates that the login logic is completeloginStatus
: Used to indicate the status of the login process: Start login (LOGIN
), login successful (LOGIN_SUCCESS
), login failed (LOGIN_ERROR
)
- The last is
switch
The statement updates the corresponding state in response to the action.
End the rest of the User’s asynchronous logic
WeChat login
In the previous section, “Implementing Redux Asynchronous Logic”, we focused on implementing the asynchronous logic of ordinary login buttons. Now let’s wrap up the logic of using wechat to log in. Open the SRC/components/WeappLoginButton/index. The js file, make corresponding modifications to the content as follows:
import Taro, { useState } from '@tarojs/taro'
import { Button } from '@tarojs/components'
import { useDispatch } from '@tarojs/redux'
import './index.scss'
import { LOGIN } from '.. /.. /constants'
export default function WeappLoginButton(props) {
const [isLogin, setIsLogin] = useState(false)
const dispatch = useDispatch()
async function onGetUserInfo(e) {
setIsLogin(true)
const { avatarUrl, nickName } = e.detail.userInfo
const userInfo = { avatar: avatarUrl, nickName }
dispatch({
type: LOGIN,
payload: {
userInfo: userInfo,
},
})
setIsLogin(false)}return (
<Button
openType="getUserInfo"
onGetUserInfo={onGetUserInfo}
type="primary"
className="login-button"
loading={isLogin}
>WeChat login</Button>)}Copy the code
As you can see, the above code has three major changes:
- We deleted the one that set login information directly
SET_LOGIN_INFO
Constant, instead of constantLOGIN
Constants. - Then we deleted the direct Settings
storage
Cache code logic - Finally, we will launch before
SET_LOGIN_INFO
Action logic changed to initiateLOGIN
Asynchronous action to handle login, and assembleduserInfo
Object as apayload
Object properties.
Since we have already processed the entire asynchronous data flow logic of LOGIN in the previous section “Implementing Redux Asynchronous Logic”, only the LOGIN action corresponding to Dispatch is needed to process the asynchronous logic of wechat LOGIN.
To optimize theuser
Logical top-level component
JSX: SRC /pages/mine/mine.jsx: SRC /pages/mine/mine.jsx
import Taro, { useEffect } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { useDispatch, useSelector } from '@tarojs/redux'
import { Header, Footer } from '.. /.. /components'
import './mine.scss'
import { SET_LOGIN_INFO } from '.. /.. /constants'
export default function Mine() {
const dispatch = useDispatch()
const nickName = useSelector(state= > state.user.nickName)
constisLogged = !! nickName useEffect((a)= > {
async function getStorage() {
try {
const { data } = await Taro.getStorage({ key: 'userInfo' })
const { nickName, avatar, _id } = data
// Update Redux Store data
dispatch({
type: SET_LOGIN_INFO,
payload: { nickName, avatar, userId: _id },
})
} catch (err) {
console.log('getStorage ERR: ', err)
}
}
if(! isLogged) { getStorage() } })return (
<View className="mine">
<Header />
<Footer />
</View>
)
}
Mine.config = {
navigationBarTitleText: 'I',}Copy the code
As you can see, we made three changes to the above code as follows:
- So first we derived it
useSelector
Hooks, obtained from Redux StorenickName
. - Then, because we saved it in the section “Implementing Redux asynchronous logic.
userId
The story of the Storeuser
Logic, so here we go fromstorage
Access to the_id
And then give it to the previous oneSET_LOGIN_INFO
的payload
Take theuserId
Properties. - Finally, let’s judge
getStorage
Only when there is no data in the Redux Store, we will get the data in the storage to update the Redux Store.
Expand Logout to clear the data range
Because the user property in the Redux Store has an extra userId, we can dispatch the Logout action to clear the userId as follows:
import Taro, { useState } from '@tarojs/taro'
import { AtButton } from 'taro-ui'
import { useDispatch } from '@tarojs/redux'
import { SET_LOGIN_INFO } from '.. /.. /constants'
export default function LoginButton(props) {
const [isLogout, setIsLogout] = useState(false)
const dispatch = useDispatch()
async function handleLogout() {
setIsLogout(true)
try {
await Taro.removeStorage({ key: 'userInfo' })
dispatch({
type: SET_LOGIN_INFO,
payload: {
avatar: ' '.nickName: ' '.userId: ' ',}})}catch (err) {
console.log('removeStorage ERR: ', err)
}
setIsLogout(false)}return (
<AtButton type="secondary" full loading={isLogout} onClick={handleLogout}>Log out</AtButton>)}Copy the code
summary
And you’re done! Here we put the user logic access to the small program cloud, and can successfully achieve the small program cloud login wechat small program end, let us immediately try to preview the effect of local debugging preview:
As can be seen, the steps for us to debug the cloud function locally and access the cloud function on the applet side are as follows:
- So let’s right click first
functions
Folder, turned on “cloud function local debugging”. - And then pick ours
login
Cloud function, then click On Enable Local debugging, so we can debug the cloud function locally. - Then we click wechat login in the small program side, and then we will see the small program developer tools console and cloud function debugging console will allow the operation of the cloud function at this time.
- Finally, we login successfully, successfully in the applet side display login nickname and avatar, and check the cloud development > Database > user table, it does add a corresponding
user
Record, indicating that we successfully connected the applets and applets cloud.
In commonly after the local debugging, we can upload function of cloud to cloud, in this way, we can not open the local debugging function to use the cloud, that there is little published online program is a must, specific cloud upload function can be in small developers tool right-click the functions provides folder corresponding to the function of cloud, Then select “Upload and deploy: Cloud Install so rely” :
In this tutorial, we implemented the asynchronous flow of User logic. In the next tutorial, we will implement the asynchronous flow of Post logic. Stay tuned!
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 it is well written, please give ❤️ a thumbs up +Github repository plus a star ❤️