This article mainly shares with you the practice of react technology stack of small application for movie collection, hoping to help everyone.
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
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
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 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:
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
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) }
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) }
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
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])) } }
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) } }
Summary, use the hook function of the component reasonably, do not process too many operations in the generator, and increase flexibility
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.
Token-based authentication process
The client initiates a login request
The server verifies the username and password
After successful verification, the server generates a token and responds to the client
The client will carry this token in the header of each subsequent request
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:
The client requests login successfully and generates a token
Save this token in redis, Set the validity period of redis (for example, 1h)
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
During the validity period, a new request comes from the client, extract the token, and update the validity period of this token in redis
The client exits the login request and deletes the token in redis
Specific code
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!