React
一些案例
案例
遍历渲染
代码示例:
// src/test/Test01.jsx
import React, {Component} from 'react';
export default class Test01 extends Component {
state = {
list: [{
id: 1,
text: '111'
}, {
id: 2,
text: '222'
}, {
id: 3,
text: '333'
}]
}
render() {
return (
<div>
<ul>
{
this.state.list.map(item =>
<li key={item.id}>{item.text}</li>)
// key是映射时必要的且唯一的
}
</ul>
</div>
);
}
}
/*
// 原生js
let list = ["aa","bb","cc"]
let newList = list.map(item=>`<li>${item}</li>`) // 自动遍历数组,每次遍历都会映射成指定字符串
console.log(newList.join("")) // 连接各个映射后的元素,否则就是个数组
*/
// src/test/Test01.jsx
import React, {Component} from 'react';
export default class Test01 extends Component {
state = {
list: [{
id: 1,
text: '111'
}, {
id: 2,
text: '222'
}, {
id: 3,
text: '333'
}]
}
render() {
let newList = this.state.list.map(item => <li key={item.id}>{item.text}</li>)
return (
<div>
<ul>
{newList}
</ul>
</div>
);
}
}
增删改查
代码示例:
// src/test/Test01.jsx
import React, {Component} from 'react';
export default class Test01 extends Component {
myRef = React.createRef()
state = {
list: [{
id: 1,
text: 'aaa'
}, {
id: 2,
text: 'bbb'
}, {
id: 3,
text: 'ccc'
}]
}
render() {
return (
<div>
<input ref={this.myRef}/>
<button onClick={() => {
console.log(this.myRef.current.value)
/*
* 不要直接修改原始状态,否则会造成不可预期的问题
* this.state.list.push(this.myRef.current.value)
* */
let newList = [...this.state.list] // 展开数据
newList.push({
id: Math.random() * 999999999,
text: this.myRef.current.value
})
this.setState({list: newList}) // 手动通知列表更新的消息,不然感知不到更新(新状态会覆盖旧状态)
this.myRef.current.value = ''
}}>提交
</button>
<ul>
{
this.state.list.map(item =>
<li key={item.id}>{item.text}</li>)
}
</ul>
</div>
)
}
}
// src/test/Test01.jsx
import React, {Component} from 'react';
export default class Test01 extends Component {
myRef = React.createRef()
state = {
list: [{
id: 1,
text: 'aaa'
}, {
id: 2,
text: 'bbb'
}, {
id: 3,
text: 'ccc'
}]
}
render() {
return (
<div>
<input ref={this.myRef}/>
<button onClick={() => {
console.log(this.myRef.current.value)
let newList = [...this.state.list]
newList.push({
id: Math.random() * 999999999999999,
text: this.myRef.current.value
})
this.setState({list: newList})
this.myRef.current.value = ''
}}>提交
</button>
<ul>
{
this.state.list.map((item, index) =>
<li key={item.id}>
{item.text}
{/* <button onClick={this.rmclick.bind(this,index)}>清除</button> */}
<button onClick={() => this.rmClick(index)}>删除</button>
</li>)
}
</ul>
</div>
)
}
rmClick(index) {
console.log('删除:', index)
// let newList = this.state.list.slice()
// let newList = this.state.list.concat()
let newList = [...this.state.list]
newList.splice(index, 1)
this.setState({
list: newList
})
}
}
// src/test/Test01.jsx
import React, {Component} from 'react';
export default class Test03 extends Component {
myRef = React.createRef()
state = {
list: [{
id: 1,
text: 'aaa'
}, {
id: 2,
text: 'bbb'
}, {
id: 3,
text: 'ccc'
}]
}
render() {
let obj = {
display: 'none'
}
return (
<div>
<input ref={this.myRef}/>
<button onClick={() => {
console.log(this.myRef.current.value)
let newList = [...this.state.list]
newList.push({
id: Math.random() * 999999999999999,
text: this.myRef.current.value
})
this.setState({list: newList})
this.myRef.current.value = ''
}}>提交
</button>
<ul>
{
this.state.list.map((item, index) =>
<li key={item.id}>
{item.text}
<button onClick={() => this.rmClick(index)}>删除</button>
</li>)
}
</ul>
<div style={this.state.list.length === 0 ? {'': ''} : obj}>暂无数据</div>
</div>
)
}
rmClick(index) {
console.log('删除:', index)
let newList = [...this.state.list]
newList.splice(index, 1)
this.setState({
list: newList
})
}
}
// src/test/Test01.jsx
import React, {Component} from 'react';
export default class Test01 extends Component {
myRef = React.createRef()
state = {
list: [{
id: 1,
text: 'aaa'
}, {
id: 2,
text: 'bbb'
}, {
id: 3,
text: 'ccc'
}]
}
render() {
return (
<div>
<input ref={this.myRef}/>
<button onClick={() => {
console.log(this.myRef.current.value)
let newList = [...this.state.list]
newList.push({
id: Math.random() * 999999999999999,
text: this.myRef.current.value
})
this.setState({list: newList})
this.myRef.current.value = ''
}}>提交
</button>
<ul>
{
this.state.list.map((item, index) =>
<li key={item.id}>
<span dangerouslySetInnerHTML={{
__html: item.text
}}/>
<button onClick={() => this.rmClick(index)}>删除</button>
</li>)
}
</ul>
</div>
)
}
rmClick(index) {
console.log("删除:", index)
let newList = [...this.state.list]
newList.splice(index, 1)
this.setState({
list: newList
})
}
}
选项卡
代码示例:
/* src/css/css01.css */
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
display: flex;
position: fixed;
bottom: 0;
left: 0;
height: 50px;
width: 100%;
line-height: 50px;
}
ul li {
flex: 1;
text-align: center;
}
.mycolor {
color: #1f1e33;
}
// src/test/Test01.jsx
import React, {Component} from 'react';
import '../css/css01.css'
class Foo extends Component {
render() {
return (
<div>
Foo
</div>
)
}
}
class Bar extends Component {
render() {
return (
<div>
Bar
</div>
)
}
}
class My extends Component {
render() {
return (
<div>
My
</div>
)
}
}
export default class Test01 extends Component {
state = {
list: [
{id: 1, text: '电影'}, {id: 2, text: '影院'}, {id: 3, text: '我的'}
], current: 0
}
which() {
switch (this.state.current) {
case 0:
return <Foo/>
case 1:
return <Bar/>
case 2:
return <My/>
default:
return null
}
}
handleclick(index) {
console.log(index)
this.setState({
current: index // 更新状态
})
}
render() {
/*
* // 内嵌CSS
* let obj = {
* background:"red",
* height:"500px",
* width:"300px",
* fontsize:"30px"
* }
* <div style={obj}>
* */
return (
<div>
{
this.which() // 当状态更新时会执行一次
}
<ul>
{
this.state.list.map((item, index) =>
<li key={item.id} className={this.state.current === index ? 'mycolor' : ''} onClick={() => {
this.handleclick(index)
}}>{item.text}</li>
)
}
</ul>
</div>
);
}
}
模糊搜索
代码示例:
/* src/css/css01.css */
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
display: flex;
position: fixed;
bottom: 0;
background-color: ghostwhite;
left: 0;
height: 50px;
width: 100%;
line-height: 50px;
}
ul li {
flex: 1;
text-align: center;
}
.mycolor {
color: #1f1e33;
}
dl {
position: relative;
top: 32px;
height: auto;
border-bottom: 1px solid #30363d;
}
dl dt {
font-size: 20px;
}
dl dd {
font-size: 12px;
color: #30363d;
}
input {
position: relative;
left: 10%;
width: 80%;
height: 50px;
line-height: 50px;
font-size: 30px;
}
// src/test/Test01.jsx
import React, {Component} from 'react';
import '../css/css01.css'
import Test02 from './Test02'
class Foo extends Component {
render() {
return (
<div>
Foo
</div>
)
}
}
class My extends Component {
render() {
return (
<div>
My
</div>
)
}
}
export default class Test01 extends Component {
state = {
list: [
{id: 1, text: '电影'}, {id: 2, text: '影院'}, {id: 3, text: '我的'}
], current: 0
}
which() {
switch (this.state.current) {
case 0:
return <Foo/>
case 1:
return <Test02/>
case 2:
return <My/>
default:
return null
}
}
handleclick(index) {
this.setState({
current: index // 更新状态
})
}
render() {
return (
<div>
{
this.which() // 当状态更新时会执行一次
}
<ul>
{
this.state.list.map((item, index) =>
<li key={item.id} className={this.state.current === index ? 'mycolor' : ''} onClick={() => {
this.handleclick(index)
}}>{item.text}</li>
)
}
</ul>
</div>
);
}
}
// src/test/Test02.jsx
import React, {Component} from 'react';
import axios from 'axios'
export default class Test02 extends Component {
constructor() {
super()
this.state = {
cinemaList: [],
backCinemaList: []
}
axios({
url: '',
method: 'get',
headers: {
'': '',
}
}).then(res => {
console.log(res.data.data.films)
this.setState({
cinemaList: res.data.data.films,
backCinemaList: res.data.data.films
})
}).catch(err => {
console.log(err)
})
}
handleInput = (event) => {
console.log('input:', event.target.value)
let newList = this.state.backCinemaList.filter(item =>
item.name.includes(event.target.value) || item.synopsis.includes(event.target.value))
this.setState({
cinemaList: newList
})
}
render() {
return (
<div>
<input onInput={this.handleInput}/>
{
this.state.cinemaList.map(item =>
<dl key={item.filmId}>
<dt>{item.name}</dt>
<dd>{item.synopsis}</dd>
</dl>
)
}
</div>
);
}
}
表单的受控与非受控
代码示例:
import React, {Component} from 'react';
export default class Test04 extends Component {
myRef01 = React.createRef()
state = {
text: ''
}
render() {
return (
<div>
<input type={'text'} ref={this.myRef01} defaultValue={'bar'}/>
<input type={'text'} ref={this.myRef01} onChange={(evt) => {
this.setState({ // 受控
text: evt.target.value
}, () => {
console.log(this.state.text)
})
}}/>
</div>
);
}
}
受控影院查询
代码示例:
// src/test/Test02.jsx
import React, {Component} from 'react';
import axios from 'axios'
export default class Test02 extends Component {
constructor() {
super()
this.state = {
cinemaList: [],
text: ''
}
axios({
url: '',
method: 'get',
headers: {
'': '',
}
}).then(res => {
console.log(res.data.data.films)
this.setState({
cinemaList: res.data.data.films,
})
}).catch(err => {
console.log(err)
})
}
getTxt() {
return this.state.cinemaList.filter(item =>
item.name.includes(this.state.text) || item.synopsis.includes(this.state.text))
}
render() {
return (
<div>
<input value={this.state.text} onChange={(evt) => {
this.setState({
text: evt.target.value
})
}}/>
{
this.getTxt().map(item =>
<dl key={item.filmId}>
<dt>{item.name}</dt>
<dd>{item.synopsis}</dd>
</dl>
)
}
</div>
);
}
}
受控组件ToDoList
代码示例:
// src/test/Test04.jsx
import React, {Component} from 'react';
export default class Test04 extends Component {
state = {
text: '',
list: [{
id: 1,
text: 'aaa',
isChecked: false
}, {
id: 2,
text: 'bbb',
isChecked: false
}, {
id: 3,
text: 'ccc',
isChecked: false
}]
}
handleChecked(index) {
let newList = [...this.state.list]
newList[index].isChecked = !newList[index].isChecked
this.setState({
list: newList
})
}
myAdd() {
let newList = [...this.state.list]
newList.push({
id: Math.random() * 999999999,
text: this.state.text,
isChecked: false
})
this.setState({
list: newList
})
}
myDel(index) {
let newList = [...this.state.list]
newList.splice(index, 1)
this.setState({
list: newList
})
}
render() {
return (
<div>
<input type={'text'} value={this.state.text} onChange={(evt) => {
this.setState({
text: evt.target.value
})
}}/>
<button onClick={() => {
this.myAdd()
}}>add
</button>
<ul>
{this.state.list.map((item, index) => <li key={item.id}>
<input type={'checkbox'} checked={item.isChecked} onChange={() => {
this.handleChecked(index)
}}/>
<span dangerouslySetInnerHTML={{
__html: item.text
}} style={{textDecoration: item.isChecked ? 'line-through' : ''}}/>
<button onClick={() => {
this.myDel(index)
}}>del
</button>
</li>)}
</ul>
<div style={this.state.list.length === 0 ? {'': ''} : {display: 'none'}}>暂无事项</div>
</div>
);
}
}
新生命周期
代码示例:
/* src/css/css03.css */
* {
margin: 0;
padding: 0;
}
// src/test/Test04.jsx
import React, {Component} from 'react';
import '../css/css03.css'
export default class Test04 extends Component {
state = {
list: [1, 2, 3, 4, 5, 6]
}
Ref = React.createRef()
getSnapshotBeforeUpdate(prevProps, prevState) {
return this.Ref.current.scrollHeight // 获取整个容器的可视高度
}
componentDidUpdate(prevProps, prevState, snapshot) {
this.Ref.current.scrollTop += this.Ref.current.scrollHeight - snapshot
// scrollTop:当前滚动条的垂直高度
}
render() {
return (
<div>
<button onClick={() => {
this.setState({
list: [...[7, 8, 9, 10], ...this.state.list]
})
}}>Click
</button>
<div style={{height: '200px', overflow: 'auto'}} ref={this.Ref}>
<ul>
{
this.state.list.map((item =>
<li key={item} style={{height: '100px', background: 'grey'}}>{item}</li>))
}
</ul>
</div>
</div>
);
}
}
轮播组件
代码示例:
// src/test/Test04.jsx
// 同步
import React, {Component} from 'react';
import Swiper, {Navigation, Pagination} from "swiper";
import 'swiper/css/bundle'
Swiper.use([Navigation, Pagination])
export default class Test04 extends Component {
state = {
list: ['aaa', 'bbb', 'ccc']
}
componentDidMount() {
new Swiper('.swiper', {
pagination: {
el: '.swiper-pagination' // 底部状态圆点
}
})
}
render() {
return (
<div>
<div className={'swiper'} style={{height: '200px', background: 'gray'}}>
<div className={'swiper-wrapper'}>
{
this.state.list.map(item =>
<div className={'swiper-slide'} key={item}>
{item}
</div>
)
}
</div>
<div className={'swiper-pagination'}/>
</div>
</div>
);
}
}
// src/test/Test04.jsx
// 异步
import React, {Component} from 'react';
import Swiper, {Navigation, Pagination} from "swiper";
import 'swiper/css/bundle'
Swiper.use([Navigation, Pagination])
export default class Test04 extends Component {
state = {
list: []
}
componentDidMount() {
setTimeout(() => {
this.setState({
list: ['aaa', 'bbb', 'ccc']
}, () => {
new Swiper('.swiper', { // 必须等DOM创建完毕才可以new
pagination: {
el: '.swiper-pagination'
}
})
})
// 在这里new也可以(setState在异步环境下是同步更新的)或者componentDidUpdate
}, 1000)
}
render() {
return (
<div>
<div className={'swiper'} style={{height: '200px', background: 'gray'}}>
<div className={'swiper-wrapper'}>
{
this.state.list.map(item =>
<div className={'swiper-slide'} key={item}>
{item}
</div>
)
}
</div>
<div className={'swiper-pagination'}/>
</div>
</div>
);
}
}
// src/test/Test04.jsx
// public/test.json
import React, {Component} from 'react';
import Swiper, {Navigation, Pagination} from "swiper";
import 'swiper/css/bundle'
import axios from "axios";
Swiper.use([Navigation, Pagination])
class Swiper01 extends Component {
componentDidMount() {
new Swiper('.swiper', {
pagination: {
el: '.swiper-pagination'
},
loop: this.props.loop, // 衔接滑动
autoplay: this.props.autoplay, // 自动轮播
navigation: { // 左右按钮
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
}
})
}
render() {
return (
<div>
<div className={'swiper'}>
<div className={'swiper-wrapper'}>
{this.props.children}
</div>
<div className={'swiper-pagination'}/>
<div className="swiper-button-prev"/>
<div className="swiper-button-next"/>
</div>
</div>
);
}
}
class Swiper02 extends Component {
render() {
return (
<div className={'swiper-slide'}>
{this.props.children}
</div>
)
}
}
export default class Test04 extends Component {
state = {
list: []
}
componentDidMount() {
axios.get(`/test.json`).then((res) => {
console.log(res.data.films)
this.setState({
list: res.data.films
})
}).catch(err => console.log(err))
}
render() {
return (
<div>
<Swiper01 loop={true} autoplay={true}>
{
this.state.list.map(item =>
<Swiper02 key={item.filmId}>
<img src={item.poster} alt={item.name} style={{height: '400px', width: '100%'}}/>
</Swiper02>
)
}
</Swiper01>
</div>
);
}
}
DOM渲染于根节点之外
代码示例:
/* src/supplement/css/portal01.css */
* {
margin: 0;
padding: 0;
}
.left, .right {
height: 100vh;
}
.box {
display: flex;
}
.left {
width: 200px;
background: #30363d;
}
.right {
flex: 1;
background: antiquewhite;
}
// src/supplement/Portal01.jsx
import React, {useState} from "react";
import './css/portal01.css'
import {createPortal} from "react-dom";
function Dialog(props) {
return (
<div style={{width: '100%', height: '100%', position: 'fixed', left: 0, top: 0, background: 'rgba(0,0,0,0.7)'}}>
<button onClick={props.onClose}>Button
</button>
</div>
)
}
export default function Portal01() {
const [state, setState] = useState(false)
return createPortal(
<div className={'box'} onClick={() => console.log('事件冒泡')}>
<div className={'left'}>
{
state && <Dialog onClose={() => {
setState(false)
}}/>
}
</div>
<div className={'right'}>
<button onClick={() => {
setState(true)
}}>Click
</button>
</div>
</div>
, document.body)
}
// export default function Portal01() {
// const [state, setState] = useState(false)
//
// return (
// <div className={'box'}>
// <div className={'left'}>
// {
// state && <Dialog/>
// }
// </div>
// <div className={'right'}>
// <button onClick={() => { // 二次点击不了(层级覆盖霸屏DOM)
// setState(true)
// }}>Click
// </button>
// </div>
// </div>
// )
// }
加载空档期显示的DOM
代码示例:
// src/supplement/Comingsoon.jsx
import React from "react";
export default function Comingsoon() {
return (
<div>
Comingsoon
</div>
)
}
// src/supplement/LazyAndSuspense.jsx
import React, {useState} from "react";
const LazyLoad = (path) => {
const Comp = React.lazy(() => import(`./${path}`))
return (
<React.Suspense fallback={<>加载ing...</>}>
<Comp/>
</React.Suspense>
)
}
function Nowplaying() {
return (
<div>
Nowplaying
</div>
)
}
export default function LazyAndSuspense() {
const [state, setState] = useState(true)
return (
<div>
<button onClick={() => {
setState(true)
}}>首页
</button>
<button onClick={() => {
setState(false)
}}>我的
</button>
{
state ? <Nowplaying/> : LazyLoad('Comingsoon')
}
</div>
)
}
Ref透传
代码示例:
// src/supplement/ForwordRef.jsx
import React, {forwardRef, useRef} from "react";
const Child = forwardRef((props, ref) => { // 第一个形参:属性,第二个形参:Ref
return (
<div>
<input type="text" ref={ref}/>
</div>
)
})
export default function ForwordRef() {
const Ref = useRef()
return (
<div>
<button onClick={() => {
console.log(Ref)
Ref.current.focus()
}}>获取焦点
</button>
<Child ref={Ref}/>
</div>
)
}
防止重复的更新
代码示例:
// src/supplement/Memo01.jsx
import React, {useState, memo} from "react";
const Child = memo((props) => {
console.log('Child')
return (
<div>{props.title}</div>
)
})
export default function Memo01() {
const [state, setState] = useState({
name: 'Foo',
title: 'Bar'
})
return (
<div>
{state.name}
<button onClick={() => {
setState({
name: 'HQSY'
})
}}>Click
</button>
<button onClick={() => {
setState({
name: 'HQSY',
title: 'test'
})
}}>Title
</button>
<Child title={state.title}/>
</div>
)
}