【前端丛林】React这样服用,效果更佳(3)

95 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

前言

哈喽大家好,我是Lotzinfly,一位前端小猎人。欢迎大家再次来到前端丛林,在这里你将会遇到各种各样的前端猎物,我希望可以把这些前端猎物统统拿下,嚼碎了服用,并成为自己身上的骨肉。今天是国庆假期第三天,也是我们冒险的第三天,昨天我们对React介绍了组件状态(State)和事件处理,今天我们会继续深入学习React,介绍一下React的生命周期、条件渲染和状态提升。在这七天假期里,让我们学会React实现弯道超车。你们准备好了吗?那么开始我们的冒险之旅吧!

1.生命周期

1.1 旧版生命周期

image.png

1.2 新版生命周期

image.png

1.2.1 getDerivedStateFromProps

static getDerivedStateFromProps(props, state) 这个生命周期的功能实际上就是将传入的props映射到state上面

import React from 'react';
import ReactDOM from 'react-dom';
class Counter extends React.Component {
    static defaultProps = {
        name: '小明'
    };
    constructor(props) {
        super();
        this.state = { number: 0 }
    }
    handleClick = () => {
        this.setState({ number: this.state.number + 1 });
    };
    render() {
        console.log('3.render');
        return (
            <div>
                <p>{this.state.number}</p>
                <ChildCounter number={this.state.number} />
                <button onClick={this.handleClick}>+</button>
            </div>
        )
    }
}
class ChildCounter extends React.Component {
    constructor() {
        super();
        this.state = { number: 0 };
    }
    static getDerivedStateFromProps(nextProps, prevState) {
        const { number } = nextProps;
        // 当传入的type发生变化的时候,更新state
        if (number % 2 == 0) {
            return { number: number * 2 };
        } else {
            return { number: number * 3 };
        }
        // 否则,对于state不进行任何操作
        return null;
    }
    render() {
        console.log('child-render', this.state)
        return (<div>
            {this.state.number}
        </div>)
    }
}
ReactDOM.render(
    <Counter />,
    document.getElementById('root')
);

1.2.2 getSnapshotBeforeUpdate

getSnapshotBeforeUpdate() 被调用于render之后,可以读取但无法使用DOM的时候。它使您的组件可以在可能更改之前从DOM捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()

import React from 'react';
import ReactDOM from 'react-dom';
class ScrollingList extends React.Component {
    constructor(props) {
        super(props);
        this.state = { messages: [] }
        this.wrapper = React.createRef();
    }
    addMessage() {
        this.setState(state => ({
            messages: [`${state.messages.length}`, ...state.messages],
        }))
    }
    componentDidMount() {
        this.timeID = window.setInterval(() => {//设置定时器
            this.addMessage();
        }, 1000)
    }
    componentWillUnmount() {//清除定时器
        window.clearInterval(this.timeID);
    }
    getSnapshotBeforeUpdate() {//很关键的,我们获取当前rootNode的scrollHeight,传到componentDidUp date 的参数perScrollHeight
        return this.wrapper.current.scrollHeight;
    }
    componentDidUpdate(perProps, perState, prevScrollHeight) {
        const curScrollTop = this.wrapper.current.scrollTop;//当前向上卷去的高度
        //当前向上卷去的高度加上增加的内容高度
        this.wrapper.current.scrollTop = curScrollTop + (this.wrapper.current.scrollHeight - prevScrollHeight);
    }
    render() {
        let style = {
            height: '100px',
            width: '200px',
            border: '1px solid red',
            overflow: 'auto'
        }
        return (
            <div style={style} ref={this.wrapper} >
                {this.state.messages.map((message, index) => (
                    <div key={index}>{message} </div>
                ))}
            </div>
        );
    }
}
ReactDOM.render(
    <ScrollingList />,
    document.getElementById('root')
);

2. 条件渲染

2.1 条件渲染

  • 可以 if 或者条件运算符去创建元素来表现当前的状态,然后让 React 根据它们来更新 UI
  • 你可以使用变量来储存元素。 它可以帮助你有条件地渲染组件的一部分
  • 你可以使用逻辑与 (&&) 和三目运算符 来进行条件渲染

2.2 列表&Key

  • 一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串
  • key 只是在兄弟节点之间必须唯一

2.3 表单受控组件和非受控组件

表单组件分为受控组件和非受控组件

  • 使 React 的 state 成为唯一数据源。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
  • 表单元素(如input、 textarea 和 select)之类的表单元素通常自己维护 state,并根据用户输入进行更新,这是非受控组件

2.4 todoApp

import React from 'react';
import ReactDOM from 'react-dom';
import { declareTypeAlias } from '@babel/types';
class Todos extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            text: '',
            todos: []
        }
    }
    handleAdd = (event) => {
        event.preventDefault();
        this.setState({
            todos: [...this.state.todos, { id: Date.now(), text: this.state.text }],
            text: ''
        });
    }
    handleDelete(id) {
        this.setState({
            todos: this.state.todos.filter(todo => todo.id != id)
        });
    }
    handleChange = (event) => {
        this.setState({
            text: event.target.value
        });
    }
    render() {
        let todos = this.state.todos.map(todo => <li key={todo.id}>{todo.text}<button onClick={() => this.handleDelete(todo.id)}>X</button></li>);
        return (
            <>
                {this.props.name && <h1>{this.props.name}</h1>}
                <form onSubmit={this.handleAdd}>
                    <input type="text" value={this.state.text} onChange={this.handleChange} />
                    <input type="submit" />
                </form>
                <ul>
                    {todos}
                </ul>
                {this.state.todos.length > 0 ? `你现在${this.state.todos.length}件待办事项` : `你现在没有待办事项`}
            </>
        );
    }
}
ReactDOM.render(
    <Todos name="待办事项" />,
    document.getElementById('root')
);

3.状态提升

import React from 'react';
import ReactDOM from 'react-dom';
class Todos extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            text: '',
            todos: []
        }
    }
    handleAdd = (event) => {
        event.preventDefault();
        this.setState({
            todos: [...this.state.todos, { id: Date.now(), text: this.state.text }],
            text: ''
        });
    }
    handleDelete = (id) => {
        this.setState({
            todos: this.state.todos.filter(todo => todo.id != id)
        });
    }
    handleChange = (event) => {
        this.setState({
            text: event.target.value
        });
    }
    render() {
        return (
            <>
                {this.props.name && <h1>{this.props.name}</h1>}
                <TodoHeader text={this.state.text} handleAdd={this.handleAdd} handleChange={this.handleChange} />
                <TodoItems todos={this.state.todos} handleDelete={this.handleDelete} />
                <TodoFooter todos={this.state.todos} />
            </>
        );
    }
}
class TodoHeader extends React.Component {
    render() {
        return (
            <form onSubmit={this.props.handleAdd}>
                <input type="text" value={this.props.text} onChange={this.props.handleChange}/>
                <input type="submit" />
            </form>
        )
    }
}
class TodoItems extends React.Component {
    render() {
        let todos = this.props.todos.map(todo => <li key={todo.id}>{todo.text}
        <button onClick={() => this.props.handleDelete(todo.id)}>X</button></li>);
        return (
            <ul>
                {todos}
            </ul>
        )
    }
}
class TodoFooter extends React.Component {
    render() {
        return (
            <>{this.props.todos.length > 0 ? `你现在${this.props.todos.length}件待办事项` : `你现在没有待办事项`}</>
        )
    }
}
ReactDOM.render(
    <Todos name="待办事项" />,
    document.getElementById('root')
);

结尾

好啦,这期的前端丛林大冒险先到这里啦!这期我们介绍了React的生命周期、条件渲染和状态提升,大家一定要好好理解,把它搞定,啃下来嚼烂嚼透。希望大家可以好好品尝并消化,迅速升级,接下来我们才更好地过五关斩六将!好啦,我们下期再见。拜拜!