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>
    )
}