Home  >  Article  >  Web Front-end  >  React technology stack practice of small application for movie collection

React technology stack practice of small application for movie collection

小云云
小云云Original
2017-12-18 15:43:061426browse
This article mainly shares with you the practice of react technology stack of small application for movie collection, hoping to help everyone.

Main functions

  • Crawl Douban movie information and enter it into MongoDB

  • Movie list display, classification, search

  • Movie details display and attachment management

  • Registration and login

  • Permission control, ordinary users can enter, Favorites, administrator entry, modification, deletion

  • User center, my favorites list

React technology stack practice of small application for movie collection

Some summary

Front-end

The front-end uses react, redux and redux-saga. Let’s briefly summarize redux and record a dependency issue on front and rear interface calls

  • redux

To sum up redux in one sentence, I think it is to entangle the vertical props transfer between components and the love-hate state between parent and child components. It's evened out, and a vertical relationship is transformed into multiple components interacting directly with an independent state object. After this, the code structure does look clearer.

The core concepts of redux, action, reducer, and store

action means that I want to operate a state. How to operate it is the reducer’s business, and all states are stored in the store. , the store issues an action and leaves it to the designated reducer for processing

Redux forces us to standardize our operations on the state, which can only be done in actions and reducers. In this way, the originally complicated business logic processing can be After changing the location and limiting it to actions and reducers, the components look very clean. In fact, it is complicated no matter where to put this complicated thing, but now it is clearer

The disadvantage of using redux is that it is too cumbersome to define various actions and connect various components. . . . . Now there is another Mobx, I don’t know how powerful it is, but everyone agrees~

  • redux-saga

redux-saga is used to handle asynchronous Calls and other things, use generators to make asynchronous code look more concise. Commonly used ones are take, takeLatest, takeEvery, put, call, fork, select. During use, there are dependencies before and after an interface call is encountered. The relationship problem is quite interesting.

Describe it:

  1. There is an interface /api/user/checkLogin, which is used to determine whether to log in. The action is triggered in componentDidMount of the outermost component to initiate this request, and if the interface return status is logged in, a

function* checkLogin() {
    const res = yield Util.fetch('/api/user/checkLogin')
    yield put(recieveCheckLogin(!res.code))
    if (!res.code) {
        //已登录
        yield put(fetchUinfo())
    }
}
export function* watchCheckLogin() {
    yield takeLatest(CHECK_LOAGIN, checkLogin)
}
is also sent to obtain user information.
  1. Then I have a movie details page component, which will launch /api/movies/${id} in componentDidMount The interface obtains movie information. If the user is logged in, it will also initiate an interface to obtain movie attachment information/api/movies/${id}/attach,The entire step is written in a generator

function* getItemMovie(id) {
    return yield Util.fetch(`/api/movies/${id}`)
}

function* getMovieAttach(id) {
    return yield Util.fetch(`/api/movies/${id}/attach`)
}

function* getMovieInfo(action) {
    const { movieId } = action
    let { login } = yield select(state => state.loginStatus)
    const res = yield call(getItemMovie, movieId)
    yield put(recieveItemMovieInfo(res.data[0]))
    if (res.data[0].attachId && login) {
        const attach = yield call(getMovieAttach, movieId)
        yield put(recieveMovieAttach(attach.data[0]))
    }
}

export function* watchLoadItemMovie() {
    yield takeLatest(LOAD_ITEM_MOVIE, getMovieInfo)
}
  1. The user logs in and enters the details. The process is normal, but if the page is refreshed on the details page , the interface for obtaining attachments is not triggered, because the checkLogin interface has not returned the result at this time, the state.loginStatus status is still false, and the above does not go to the if

  2. At first I was thinking about how to control the sequence of yields in some generators to solve the problem (if the user is not logged in, send another CHECK_LOAGIN, and the process will return to continue), but there are CHECK_LOAGIN calls twice. If the user is logged in, it will be called again. It is definitely not possible to call the interface for obtaining user information once more.

function* getMovieInfo(action) {
    const { movieId } = action
    let { login } = yield select(state => state.loginStatus)
    const res = yield call(getItemMovie, movieId)
    yield put(recieveItemMovieInfo(res.data[0]))
    // if (!login) {
    //     //刷新页面的时候,如果此时checklogin接口还没返回数据或还没发出,应触发一个checklogin
    //     //checklogin返回后才能得到login状态
    //     yield put({
    //         type: CHECK_LOAGIN
    //     })
    //     const ret = yield take(RECIEVE_CHECK_LOAGIN)
    //     login = ret.loginStatus
    // }
    if (res.data[0].attachId && login) {
        const attach = yield call(getMovieAttach, movieId)
        yield put(recieveMovieAttach(attach.data[0]))
    }
}
  1. The final method is to decompose the responsibilities of the generator and properly trigger the action of obtaining attachments in componentWillUpdate.

//将获取附件的动作从 getMovieInfo这个generator中分离出来
function* getMovieInfo(action) {
    const { movieId } = action
    const res = yield call(getItemMovie, movieId)
    yield put(recieveItemMovieInfo(res.data[0]))
}
function* watchLoadItemMovie() {
    yield takeLatest(LOAD_ITEM_MOVIE, getMovieInfo)
}
function* watchLoadAttach() {
    while (true) {
        const { movieId } = yield take(LOAD_MOVIE_ATTACH)
        const { attachId } = yield select(state => state.detail.movieInfo)
        const attach = yield call(getMovieAttach, movieId)
        yield put(recieveMovieAttach(attach.data[0]))
    }
}

//组件中
componentWillUpdate(nextProps) {
        if (nextProps.loginStatus && (nextProps.movieInfo!==this.props.movieInfo)) {
            //是登录状态,并且movieInfo已经返回时
            const { id } = this.props.match.params
            this.props.loadMovieAttach(id)
        }
}
  1. Summary, use the hook function of the component reasonably, do not process too many operations in the generator, and increase flexibility

Backend

The backend uses express and mongodb, and also uses redis. The main technical points areUse pm2 to manage node applications and deploy code, enable identity authentication in mongodb, and use token+ Redis is used for identity authentication and unit tests are written in node. It is still worth recording.

  • Use jwt + redis for token-based user identity authentication

Token-based authentication process

  1. The client initiates a login request

  2. The server verifies the username and password

  3. After successful verification, the server generates a token and responds to the client

  4. The client will carry this token in the header of each subsequent request

  5. The server needs to verify the token for the interface that requires authentication, and the verification successfully receives the request

Here jsonwebtoken is used to generate the token,

jwt.sign(payload, secretOrPrivateKey, [options, callback])

is used express-jwt verification token (successful verification will put the token information in request.user)

express_jwt({
        secret: SECRET,
        getToken: (req)=> {
        if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
            return req.headers.authorization.split(' ')[1];
        } else if (req.query && req.query.token) {
            return req.query.token;
        }
        return null;
    }
    }

Why use redis

**When using jsonwebtoken to generate a token, you can specify the validity period of the token, and the verify method of jsonwebtoken also provides options to update the validity period of the token.
But express_jwt middleware is used here, and express_jwt does not provide a method to refresh the token. **

Idea:

  1. The client requests login successfully and generates a token

  2. Save this token in redis, Set the validity period of redis (for example, 1h)

  3. When a new request comes, first express_jwt verifies the token, the verification is successful, and then verifies whether the token exists in redis, the existence indicates that it is valid

  4. During the validity period, a new request comes from the client, extract the token, and update the validity period of this token in redis

  5. The client exits the login request and deletes the token in redis

Specific code

  • Use mocha + supertest + should to write unit tests

Test coverage I wrote all the interfaces. During development, I wrote them slowly because there were no progress requirements. After writing an interface, I wrote a test. The test writing was quite detailed. After the test passed, I then adjusted the interface on the front end. The whole process was quite interesting.

mocha is a node unit testing framework, similar to the front-end jasmine, and the syntax is also similar

supertest is a library used to test the node interface

should nodejs assertion library, very readable

An example of testing, the length is too long, so I won’t put it here

Related recommendations :

Exploring the internal mechanism of React

What are the ways to write components in React

react.js identifies ref, get the content


The above is the detailed content of React technology stack practice of small application for movie collection. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn