持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
前言
哈喽大家好,我是Lotzinfly,一位前端小猎人。欢迎大家再次来到前端丛林,在这里你将会遇到各种各样的前端猎物,我希望可以把这些前端猎物统统拿下,嚼碎了服用,并成为自己身上的骨肉。今天是国庆假期第三天,也是我们冒险的第三天,昨天我们对React介绍了组件状态(State)和事件处理,今天我们会继续深入学习React,介绍一下React的生命周期、条件渲染和状态提升。在这七天假期里,让我们学会React实现弯道超车。你们准备好了吗?那么开始我们的冒险之旅吧!
1.生命周期
1.1 旧版生命周期
1.2 新版生命周期
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的生命周期、条件渲染和状态提升,大家一定要好好理解,把它搞定,啃下来嚼烂嚼透。希望大家可以好好品尝并消化,迅速升级,接下来我们才更好地过五关斩六将!好啦,我们下期再见。拜拜!