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
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.
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
can be solved by adding two fields to the tsconfig.json file.
One is baseUrl and the other is path.
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
prettier configuration
.prettierrc file
{
"useTabs": false,
"tabWidth": 2,
"printWidth": 80,
"singleQuote": true,
"trailingComma": "none",
"semi": false
}
Introduction to configuration items
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
Search editor default in vscode's file-preferences-settings
to find the formatting configuration.
Select prettier
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
. A modular specification for choosing esmodule or common js. Generally,
if esmodule uses ts, will it prompt you to install ts's eslint dependency? Generally choose yes
Then a .eslintrc.js file will be generated
. Install the eslint plug-
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
in the configuration.
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.alwaysShowStatus": true,
"eslint.validate":[
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
]
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.
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.
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
fragment. The shortcut code
will be generated. Copy the code into the user code snippet in VsCode
Enter typescriptreact and select
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.
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.
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.
Solution
, 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
. 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
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
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.
Use css to prevent the last vertical bar from displaying
Solved
in the item selector
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.
Solution:
Use the non-null assertion operator of ts
audioRef.current!.src = getSongPlayUrl(currentSong.id)