此文项目代码:https://github.com/bei-yang/I-want-to-be-an-architect
码字不易,辛苦点个star,感谢!
引言
此篇文章主要涉及以下内容:
-
react基础语法 - 官方
create-react-app脚手架 -
JSX语法 setState-
react生命周期 -
props传递参数 -
react组件通信
学习资源
知识点
事件监听
React中使用onClick类似的写法来监听事件,注意this绑定问题react里严格遵循单向数据流,没有数据双向绑定,所以输入框要设置value和onChange
handleChange(e){
this.setState({
name:e.target.value
})
}
// 写法1 箭头函数自动修正this
<input
type="text"
value={this.state.name}
onChange={(e)=>this.handleChange(e)}
/>
// 写法2 需要在构造函数里手动绑定this,否则会报错
this.addGood = this.addGood.bind(this);
addGood() {
this.setState(prevState => {
return {
goods: [
...prevState.goods,
{
id: prevState.goods.length + 1,
text: prevState.text
}
]
};
});
}
// 写法3
handleChange=(e)=>{
this.setState({
name:e.target.value
})
}
组件通信
做个小购物车
所有的处理尽可能在父组件处理,子组件抽为一个无状态组件。
import React, { Component } from "react";
export default class CartSample extends Component {
// 状态初始化一般放在构造器中
constructor(props) {
super(props);
this.state = {
goods: [
{ id: 1, text: "web全栈架构师" },
{ id: 2, text: "python全栈架构师" }
],
text: "",
cart: []
};
this.addGood = this.addGood.bind(this);
}
// 回调函数声明为箭头函数
textChange = e => {
this.setState({ text: e.target.value });
};
addGood() {
this.setState(prevState => {
return {
goods: [
...prevState.goods,
{
id: prevState.goods.length + 1,
text: prevState.text
}
]
};
});
}
// 加购函数
addToCart = good => {
// 创建新购物车
const newCart = [...this.state.cart];
const idx = newCart.findIndex(c => c.id === good.id);
const item = newCart[idx];
if (item) {
newCart.splice(idx, 1, { ...item, count: item.count + 1 });
} else {
newCart.push({ ...good, count: 1 });
}
// 更新
this.setState({ cart: newCart });
};
// 处理数量更新
add = good => {
// 创建新购物车
const newCart = [...this.state.cart];
const idx = newCart.findIndex(c => c.id === good.id);
const item = newCart[idx];
newCart.splice(idx, 1, { ...item, count: item.count + 1 });
// 更新
this.setState({ cart: newCart });
};
minus = good => {
// 创建新购物车
const newCart = [...this.state.cart];
const idx = newCart.findIndex(c => c.id === good.id);
const item = newCart[idx];
newCart.splice(idx, 1, { ...item, count: item.count - 1 });
// 更新
this.setState({ cart: newCart });
};
render() {
// const title = this.props.title ? <h1>this.props.title</h1> : null;
return (
<div>
{/* 条件渲染 */}
{this.props.title && <h1>{this.props.title}</h1>}
{/* 列表渲染 */}
<div>
<input
type="text"
value={this.state.text}
onChange={this.textChange}
/>
<button onClick={this.addGood}>添加商品</button>
</div>
<ul>
{this.state.goods.map(good => (
<li key={good.id}>
{good.text}
<button onClick={() => this.addToCart(good)}>加购</button>
</li>
))}
</ul>
{/* 购物车 */}
<Cart data={this.state.cart} minus={this.minus} add={this.add} />
</div>
);
}
}
function Cart({ data, minus, add }) {
return (
<table>
<tbody>
{data.map(d => (
<tr key={d.id}>
<td>{d.text}</td>
<td>
<button onClick={() => minus(d)}>-</button>
{d.count}
<button onClick={() => add(d)}>+</button>
</td>
{/* <td>{d.price*d.count}</td> */}
</tr>
))}
</tbody>
</table>
);
}
大名鼎鼎的虚拟DOM
浏览器渲染图
dom操作成本实在是太高,所以才有了在js里模拟和对比,JSX里使用react.createElement构架虚拟dom,每次有修改,先对比js里的虚拟dom树
生命周期
React V16.3之前的生命周期
import React, { Component } from "react";
export default class Lifecycle extends Component {
constructor(props) {
super(props);
// 常用于初始化状态
console.log("1.组件构造函数执行");
}
componentWillMount() {
// 此时可以访问状态和属性,可进行api调用等
console.log("2.组件将要挂载");
}
componentDidMount() {
// 组件已挂载,可进行状态更新操作
console.log("3.组件已挂载");
}
componentWillReceiveProps() {
// 父组件传递的属性有变化,做相应响应
console.log("4.将要接收属性传递");
}
shouldComponentUpdate() {
// 组件是否需要更新,需要返回布尔值结果,优化点
// 可以拿到props、state,当props或者state没有更新时,return false做优化
console.log("5.组件是否需要更新?");
return true;
}
componentWillUpdate() {
// 组件将要更新,可做更新统计
console.log("6.组件将要更新");
}
componentDidUpdate() {
// 组件更新
console.log("7.组件已更新");
}
componentWillUnmount() {
// 组件将要卸载, 可做清理工作
console.log("8.组件将要卸载");
}
render() {
console.log("组件渲染");
return <div>生命周期探究</div>;
}
}
激活更新阶段:App.js
import Lifecycle from "./【React】react入门1/react01/src/components/Lifecycle";
class App extends Component{
state={prop:'some content'};
componentDidMount(){
this.setState({props:'new Content'});
}
render(){
return (
<div>
<Lifecycle prop={this.state.prop}></Lifecycle>
</div>
)
}
}
激活卸载阶段,App.js
import Lifecycle from './【React】react入门1/react01/src/components/Lifecycle'
class App extends Component {
state = { prop: 'some content' }
componentDidMount() {
this.setState({ props: 'new Content' })
setTimeout(() => {
this.setState({ prop: '' })
}, 2000)
}
render() {
return (
<div>
{this.state.prop && <Lifecycle prop={this.state.prop}></Lifecycle>}
</div>
)
}
}
React v16.4的生命周期
变更缘由
原来( React v16.0之前)的生命周期在React v16推出的Fiber之后就不合适了,因为如果要开启async rendering,在render函数之前的所有函数,都有可能被执行多次。
原来(React v16.0前)的生命周期有哪些是在render前执行的呢?
- componentWillMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
如果开发者开了async rendering,而且又在以上这些render前执行的生命周期方法做AJAX请求的话,那AJAX将被无谓的多次调用。明显不是我们期望的结果,而且在componentWillMount里发起AJAX,不管多快得到结果也赶不上首次render,而且componentWillMount在服务器端渲染也会被调用到(当然,也许这是预期的结果),这样的IO操作放在componentDidMount里更合适。
禁止不能用比劝导开发者不要这样用的效果更好,所以除了shouldComponentUpdate,其他在render函数之前的所有函数(componentWillMount,componentWillReceiveProps,componentWillUpdate)都被getDerivedStateFromProps替代。
也就是用一个静态函数getDerivedStateFromProps来取代被deprecate的几个生命周期函数,就是强制开发者在render之前只做无副作用的操作,而且能做的操作局限在根据props和state决定新的state。
React v16.0刚推出的时候,是增加了一个componentDidCatch生命周期函数,这只是一个增量式修改,完全不影响原有生命周期函数;但是,到了React v16.3,大改动来了,引入了两个新的生命周期函数。
新引入了两个新的生命周期函数:getDerivedStateFromProps,getSnapshotBeforeUpdate
getDerivedStateFromProps
static getDerivedStateFromProps(props, state)在组件创建时和更新时的render方法之前调用,它应该返回一个对象来更新状态,或者返回null来不更新任何内容。
getSnapshotBeforeUpdate
getSnapshotBeforeUpdate() 被调用于render之后,可以读取但无法使用DOM的时候。它使您的组件可以在可能更改之前从DOM捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。
官网给的例子:
class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
//我们是否要添加新的 items 到列表?
// 捕捉滚动位置,以便我们可以稍后调整滚动.
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
//如果我们有snapshot值, 我们已经添加了 新的items.
// 调整滚动以至于这些新的items 不会将旧items推出视图。
// (这边的snapshot是 getSnapshotBeforeUpdate方法的返回值)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}
你的赞是我前进的动力
求赞,求评论,求分享...









网友评论