react18-code specification-ts-imitation NetEase cloud project

Project creation

Because when react creates a project, unlike vue, which will prompt you to follow xxx, the project created by react by default does not contain other things. When we want to integrate typescript, we can add the parameter
create-react-app yh_music --template typescript
to delete some useless files. Become the picture below
Insert image description here

We will find what is the function of a file
react-app-env.d.ts?

This file references TypeScript type declarations specific to projects launched with Create React App.

These type declarations add support for importing resource files such as bmp, gif, jpeg, jpg, png, webp, and svg. This means that the following imports will work as expected without errors:

import logo from './logo.svg';

It also adds support for importing CSS modules. This has to do with importing files with the extension .module.css, .module.scss and .module.sass.

Cancel strict mode.
React.StrictMode can be deleted in index.jsx to avoid refreshing sub-components twice.
Insert image description here

Configure the alias and use craco to modify the webpack configuration.
Configure the same as the Airbnb project.
Note that an error will be reported after configuration. ts will report an error after using the @ symbol. This
Insert image description here
can be solved by adding two fields to the tsconfig.json file.
One is baseUrl and the other is path.
Insert image description here

Code specifications

 EditorConfig: Focuses on unifying editor coding style configuration
 Prettier: Focuses on checking and automatically correcting code style, beautifying code
 Eslint: Focusing on JavaScript code quality inspection, coding style constraints, etc.

Integrated editconfig configuration

Editconfig helps maintain a consistent coding style for multiple developers working on the same project on different IDE editors
npm install perttier -D
.editorconfig file

# http://editorconfig.org

root = true

[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space)
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行尾的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行

[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off
trim_trailing_whitespace = false

Then install this plugin
Insert image description here

prettier configuration

.prettierrc file

{
    
    
  "useTabs": false,
  "tabWidth": 2,
  "printWidth": 80,
  "singleQuote": true,
  "trailingComma": "none",
  "semi": false
}

Introduction to configuration items
Insert image description here

When we use ctrl+s to save after configuring, we will find that the prettier rules have been triggered.
We now add the following code to the script in package.json.

"prettier":"prettier --write ."

Then configure a .prettierignore file, because the above command causes all codes to perform prettier formatting, but some files in nodemodules do not need to be formatted, so the ignore file .prettierignore must be configured
.

/build/*
.local
.output.js
/node_modules/**

**/*.svg
**/*.sh

/public/*

Execute npm run perttier and everything will be formatted.
One more thing needs to be configured, install this plug-in
Insert image description here

Search editor default in vscode's file-preferences-settings
Insert image description here
to find the formatting configuration.
Insert image description here
Select prettier
Insert image description here
and the configuration is ready. When we use ctrl+s to save, the prettier configuration will be triggered.

eslint configuration

Install
npm install eslint -D
for configuration, but manual configuration is too troublesome and there are many configurations. Automatic configuration is used here.
Execute
npx eslint --init
and the following picture will appear. Select the second To check syntax and find problems, check the syntax and find problems
Insert image description here
. A modular specification for choosing esmodule or common js. Generally,
Insert image description here
if esmodule uses ts, will it prompt you to install ts's eslint dependency? Generally choose yes
Insert image description here

Then a .eslintrc.js file will be generated
. Install the eslint plug-
Insert image description here
in. Click the middle button in File-Preferences-Settings in vscode (hovering the mouse will prompt you to open the button in the settings) and add it
Insert image description here
in the configuration.

"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.alwaysShowStatus": true,
"eslint.validate":[
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
]

Insert image description here

To configure the conflict between eslint and prettier,
install
npm install eslint-plugin-prettier eslint-config-prettier -D
and add the following code to .eslintrc.js, which means prettier will be detected at the same time when detecting eslint.
Insert image description here
If it is not configured like this, if you write anything in the vs editor that does not meet the prettier specifications, eslint will not report an error, but will only report an error during compilation.
Vue scaffolding has been set up for us, but React does not, so the process of building it from scratch will be a bit cumbersome.

CSS style reset

normalize.css configuration
install normalize.css

npm install normalize.css
and then write in index.ts

import 'normalize.css'

reset.css configuration
Then write a less file in the css folder under the assets folder, which can be called a public file.
Import in index.ts
Note: The default react does not support less, so install
npm install @craco/craco@alpha -D
and introduce it in craco.config.js

const CracoLessPlugin = require('craco-less')

Write in the object exported by module.exports

  plugins: [
    {
    
    
      plugin: CracoLessPlugin,
      options: {
    
    
        lessLoaderOptions: {
    
    
          lessOptions: {
    
    
            javascriptEnabled: true
          }
        }
      }
    }
  ],

Routing configuration

index.tsx file

import React from 'react'
import {
    
     Navigate } from 'react-router-dom'
import type {
    
     RouteObject } from 'react-router-dom'

const routes: RouteObject[] = [
  {
    
    
    path: '/',
    element: <Navigate to="/discover" />
  }
]

export default routes

Generate code component segments containing ts, and directly generate shortcut keys next time

First generate a code snippet template on snippet creator
online snippet creator

Then copy the code template we need to quickly generate.
Insert image description here
Here, change Home to ${Home} and copy it to the area below. The reason for the change is that after the change, the cursor will be automatically positioned at the Home input trigger Insert image description here
fragment. The shortcut code
Insert image description here
will be generated. Copy the code into the user code snippet in VsCode
Insert image description here
Enter typescriptreact and select
Insert image description here

Just copy the code into the file and
then enter the shortcut key to find that the new code segment can be generated.

chiildren attribute of props

In the early days, we did not need to define the children attribute on IProps. React brought it for us by default, but now we need to manually specify the children attribute. If not specified, it means that you cannot write to the slot when using the Discover component. Its type is a ReatNode because we are not sure what type was passed.
Insert image description here

After the route is lazy loaded and the secondary routing component is switched, the page will flash.

This is because Suspense only processes the first-layer routing. When we also do lazy loading for the second-level routing, our second-level routing is not processed by Suspense, so we also wrap the second-level routing with Suspense. The following picture
is On our Discover page, when lazily loading the sub-components of the Discover page, we need to wrap the placeholder element Outlet with Suspense.
Insert image description here

Redux state management

Download toolkit and react-redux
npm install @reduxjs/toolkit react-redux
and then configure it in the store.
Then use the provider package after importing it in index.ts. In
the component, state is introduced through useselector
, but using useselector reports an error that the state is unknown.
Insert image description here

Solution
Insert image description here
, but we have to write any every time. We hope that the type can be automatically deduced.
Write a TypedUseSelectorHook in the index.ts of the store.

import {
    
    
  useSelector,
  useDispatch,
  TypedUseSelectorHook,
  shallowEqual
} from 'react-redux'
//拿到store.getState的类型
type GetStateFnType = typeof store.getState
//拿到store.getState函数的返回值类型
export type IRootState = ReturnType<GetStateFnType>
type DispatchType = typeof store.dispatch

// useAppSelector的hook,函数调用签名的方式
export const useAppSelector: TypedUseSelectorHook<IRootState> = useSelector
export const useAppDispatch: () => DispatchType = useDispatch
export const shallowEqualApp = shallowEqual

This way you can use TypedUseSelectorHook instead of useSelector.
In fact, you can use the official ones for useDispatch and shallowEqual, or you can use your own encapsulation.

Convert json interface data to ts type

json interface data to ts type online link

ts use of useState

Define the corresponding array type

interface bannertype {
    
    
  name: string
  age: number
}

When used, it is passed in through generics, indicating that the type of banner is bannertype[]

const [banner, setbanner] = useState<bannertype[]>([])

Use of class component ts

Pass in the generic type after PureComponent. The first parameter is the type of props, and the second parameter is the type of state. The
constructor can be simplified in the class. Instead of writing it directly, use the member declaration to directly write state={ height: '1.88', sex : 'man'} Simplify the writing of this.state

import React, {
    
     PureComponent } from 'react'
interface Iprops {
    
    
  name: string
  age: number
}
interface Istate {
    
    
  height: string
  sex: string
}
export default class a extends PureComponent<Iprops, Istate> {
    
    
  state = {
    
    
    height: '1.88',
    sex: 'man'
  }
  // constructor(props: Iprops) {
    
    
  //   super(props)
  //   this.state = {
    
    
  //     height: '1.88',
  //     sex: 'man'
  //   }
  // }
  render() {
    
    
    return (
      <div>
        <h1>{
    
    this.props.age}</h1>
        <h1>{
    
    this.state.height}</h1>
      </div>
    )
  }
}

ts third-party library introduction error

We introduced an error after install
Insert image description here
. Reason: No type declaration.
Solution:
npm i --save-dev @types/styled-components

Class name used globally

If we use a certain class or some sprite class names a lot, we don’t need to write them in styled-components. We can directly write the corresponding class into the globally introduced common.css, and then add the class on the corresponding jsx element. Just the name will do.
common.css file
Insert image description here

The state of useState will be initialized after clicking the refresh button of the page, which is equivalent to reloading the value of useState. However, the component update triggered by setState will save the value of State.

Requirement: Refresh the page after clicking the tab menu, and keep the status before clicking the tab menu bar.

Just use the NavLink component

Use antd carousel component

First, if you want to use the api function provided by the component, you need to bind the ref. How do you provide the type at this time?
Introducing types and Carousel components

import type {
    
     FC, ReactNode, ElementRef } from 'react'
import {
    
     Carousel } from 'antd'

Passing in generics, ElementRef receives another generic

  const bannerRef = useRef<ElementRef<typeof Carousel>>(null)

Error reported after using api
Insert image description here
solution

  function handlePrevClick() {
    
    
    bannerRef.current?.prev()
  }
  function handleNextClick() {
    
    
    bannerRef.current?.next()
  }

style>Writing

For example, the content selector represents the direct child element of this RecommendWrapper and will not interfere with the style of the nested .content class selector.
Insert image description here

Use css to prevent the last vertical bar from displaying

Insert image description here
Solved
Insert image description here
in the item selector
Insert image description here

data processing

Display five pieces of data in a carousel chart. Click the switch button to switch to the next five pieces
. Use slice cutting and pass in [0, 1] for item because there are only ten pieces of data.

  <Carousel ref={
    
    bannerRef} dots={
    
    false} speed={
    
    1500}>
    {
    
    [0, 1].map((item) => {
    
    
      return (
        <div key={
    
    item}>
          <div className="album-list">
            {
    
    newAlbums.slice(item * 5, (item + 1) * 5).map((album) => {
    
    
              return <NewAlbumItem key={
    
    album.id} itemData={
    
    album} />
            })}
          </div>
        </div>
      )
    })}
  </Carousel>

Music player implementation ideas

layout

First, use fixed positioning to fix the music player to the bottom of the screen. Click to switch the icon to pause playback to set an isPlaying variable, and pass in styled-components to control the switching of images. Inside the music player is a progress bar component and a native radio html element.
Use useAppSelector to obtain song information, including name, duration, etc.

Requirement 1: Click the play icon to play the song and make the progress bar move slowly.
1. After clicking, control isPlaying to true and switch the icon.
2. Radio comes with an event onTimeUpdate. This function will be executed every moment when the radio is played, and then In this function, you can get the current playing time of the radio in milliseconds through audioRef.current!.currentTime, and then set this time as the playing time of the progress bar, then calculate the percentage of song playing, and assign it to the progress of the progress bar. Can.

Requirement 2: Click on the progress bar, the song switches progress in real time, and then the progress bar moves slowly to continue playing. First,
clicking on the progress bar will trigger a progress bar component with its own event handleSliderChanged. Inside this function, we can get the percentage of the progress bar and change the percentage Set as progress bar progress. The percentage of the progress bar is multiplied by the total number of milliseconds to get the current time, which is assigned to the playback time of the progress bar.

Requirement 3: Realize dragging the progress bar, the song switches progress in real time, and then the progress bar moves slowly to continue playing.
First, dragging the progress bar will trigger a handleSliderChanging event that comes with the progress bar component. First, we need to declare that the current state is dragging, which can be useful. A state is recorded. Change the state to true inside this function, which represents dragging (to prevent the onTimeUpdate event from being triggered all the time to change the progress of the progress bar). The rest requires the same two steps.

Requirement 4: Match the corresponding lyrics and display them when the song is played.
First, we parse the obtained lyrics data into an object form. This object contains the milliseconds when the lyrics started to be played and the content of the lyrics.
Then it will be stored in redux, and the onTimeUpdate event will be triggered when our song is playing. Inside the function, we traverse the lyrics. If the milliseconds of the lyrics are greater than the current milliseconds, then break and record the index, thus finding the lyrics we want.

Requirement 5: Avoid refreshing the components that display lyrics multiple times when matching lyrics.
When the onTimeUpdate function matches the index of the lyrics, write a judgment. If the current song is still playing this lyric, then the index will not change, and then 1return will be changed. Then we store it in redux, so that the index will not be stored in redux many times when playing a lyric.

if (lyricIndex === index || index === -1) return
dispatch(changeLyricIndexAction(index))

Requirement 6: Switching between songs and lyrics.
First, we store a playSongList in redux, representing all songs, and then store a playSongIndex to represent the currently playing index. When we play a song, we store playSongList and playSongIndex. When we switch to the next song When calling changeMusicAction in redux, passing in the isNext parameter, what this function does is to first determine the playback mode, and then randomize a newindex if it is random, get a new song from the playSongList and then dispatch to change the current song. If it is played sequentially and isNext is true (representing the next song), the index+1 is used, and then the same as above. Then the switching of lyrics is to call the interface to obtain the latest lyrics and save them in redux.

Requirement 7: Switching of playback mode.
Design a variable in redux to represent the playback mode, save it in redux, switch the playback mode of redux in the click function of the icon, and pass the playback mode into css to control the icon display.

The src attribute exists on audioRef.current, but the eslint check reports an error.

Insert image description here
Solution:
Use the non-null assertion operator of ts

audioRef.current!.src = getSongPlayUrl(currentSong.id)

Guess you like

Origin blog.csdn.net/wyh666csdn/article/details/128676975