目录
React 快速上手 - 04 基础特性 JSX、Props、State、Lifecycle、Event、Style
目标
掌握 react 的基础特性
- 语法 JSX
- 属性 Props
- 状态 State
- 生命周期 Lifecycle
- 事件 Event
- 样式 Style
基础特性
react.js 本身只是一个精简的类库,提供了几个特性或者说是工具,每个话题深入都可以长篇大论。
我这里只关注使用,毕竟轮子造出来还是用的,而不是观赏。
1. JSX 语法
我的理解 jsx 就是 html + 表达式
的混合体
1.1 请用 ( ... )
把 jsx 代码包含起来
const Element1 = () => (<h2>组件1 - 常量</h2>)
这样写的理由
- 低版本的兼容性
- 多行书写不会报错
当然新版里如果你单行可以省略
1.2 必须有个顶级标签
错误
let Element4 = () => {
return (
<h2>组件4 - es6 箭头函数</h2>
<h2>组件4 - es6 箭头函数</h2>
)
}
正确
let Element4 = () => {
return (
<div>
<h2>组件4 - es6 箭头函数</h2>
<h2>组件4 - es6 箭头函数</h2>
</div>
)
}
如果你只有一个标签,自己本身就是顶级标签,无需加
1.3 { ... }
开始你的js表达式
function ElementShow(props) {
return (
<div>
<p>字符串: {props.name} </p>
<p>日期变量: {props.date.toLocaleTimeString()}</p>
</div>
)
}
分别打印 属性值、时间函数
1.4 对于没有子元素的标签来说,请关闭标签
<div>
<ElementProps />
</div>
结束符 /
codepen
https://codepen.io/ducafecat/...
2. 属性 Props
2.1 属性值 都是只读的
function ElementShow(props) {
props.isShow = true // 只读不能修改
...
打印截图
2.2 特殊的几个 属性值
记忆就行,和我们之前写 html 有些差异
我看着都是因为 js 中保留字关系
jsx | html |
---|---|
tabIndex | index |
className | class |
htmlFor | for |
示例
const ElementProps = () => (
<div tabIndex="0" className="divbg" >
JSX 属性 tabIndex、className
</div>
)
2.3 默认使用驼峰格式 camelCase
错误
<Foo
UserName="hello"
phone_number={12345678}
/>
正确
<Foo
userName="hello"
phoneNumber={12345678}
/>
2.4 如果属性值为 true, 可以直接省略
错误
<Foo
hidden={true}
/>
正确
<Foo
hidden
/>
2.5 key
属性是怎么回事
示例
<ul>
{NavRoutes.map((route, index) => (
<li key={route.id}>
{route.title}
</li>
))}
</ul>
如果不写呢
看来是绕不过的
总结
react利用key来识别组件,它是一种身份标识标识
同层的唯一就行,不用全局唯一
避免使用数组的index作为key
值
2.6 防注入攻击
代码
const jsContent = `
<script type="text/javascript">
alert("JSX 防注入攻击!")
</script>`
const ElementInject = () => <div>{jsContent}</div>
打印
内容在渲染之前都被转换成了字符串,这样可以有效地防止 XSS(跨站脚本) 攻击
2.7 childen
表示子节点对象
这个属性表示,当前组件嵌套的对象集合
render() {
return(
<RadioGroup>
<RadioButton value="first">First</RadioButton>
<RadioButton value="second">Second</RadioButton>
<RadioButton value="third">Third</RadioButton>
</RadioGroup>
)
}
RadioGroup
的props.childen
就是这三个RadioButton
你可能会问这有什么用,我们可以用来加工呀,各种循环、克隆、修改,当然我是不太推荐这样去修改 dom 对象
codepen
https://codepen.io/ducafecat/...
3. 状态 State
组件内部的数据管理对象,自动状态控制
有的同学可能对 MVVM
比较了解,一定会说 React
怎么没有双向绑定
这可能就是设计思想问题了,不想给工具赋予过多负重,轻巧才灵活,下一章,我会通过一个函数解决双向绑定来处理表单操作,就几行代码
这里我们还是谈谈基础操作,懂得同学可以 PASS
3.1 初始化
class ElementStateStatic extends Component {
constructor(props) {
super(props)
this.state = {date: new Date()}
}
render() {
return <p>初始时间 => {this.state.date.toLocaleString()}</p>
}
}
constructor
是组件构造函数,给this.state
初始值时 要用key/val
形式的对象jsx
中使用时直接this.state.data
对象调用即可
3.2 更新
class ElementStateUpdate extends Component {
constructor(props) {
super(props)
this.date = props.date
this.state = {date: new Date()}
}
componentDidMount() {
if (this.date !== undefined) {
// 传值方式
this.setState({date: this.date})
// 函数方式
// this.setState((state, props) => {
// return {date: this.date}
// })
}
}
render() {
return <p>更新时间 => {this.state.date.toLocaleString()}</p>
}
}
需要使用 this.setState
函数设置
简单操作,直接 传入 key / value
的对象
- 如果需要之前的
state
或者组件属性props
,需要写成
this.setState((state, props) => {
let date = state.data
date = date.addDay(10)
return {date}
})
codepen
https://codepen.io/ducafecat/...
4. 生命周期 Lifecycle
4.1 组件有三种状态
状态 | 说明 |
---|---|
Mount | 已插入真实 DOM |
Update | 正在被重新渲染 |
Unmount | 已移出真实 DOM |
4.2 组件周期函数
状态 | 说明 |
---|---|
componentDidMount | 在第一次渲染后调用,只在客户端。 |
shouldComponentUpdate | 返回一个布尔值。在组件接收到新的props或者state时被调用。 |
componentDidUpdate | 在组件完成更新后立即调用。在初始化时不会被调用。 |
componentWillUnmount | 在组件从 DOM 中移除的时候立刻被调用。 |
getDerivedStateFromProps | 组件实例化后和接受新属性时将会调用 新增 |
getSnapshotBeforeUpdate | 在最新的渲染输出提交给DOM前将会立即调用 新增 |
4.3 示例打印周期过程
代码
class ElementLifecycle extends Component {
constructor(props) {
super(props)
this.date = props.date
this.state = {date: this.date}
}
componentDidMount() {
console.log('componentDidMount 在第一次渲染后调用')
if (this.date !== undefined) {
this.setState({date: this.date})
}
}
shouldComponentUpdate(nextProps, nextState) {
console.log(
'shouldComponentUpdate 在组件接收到新的props或者state时被调用',
nextProps,
nextState
)
return true // 返回一个布尔值,大家可以试着在这里返回 false
}
componentDidUpdate(prevProps, prevState) {
console.log(
'componentDidUpdate 在组件完成更新后立即调用',
prevProps,
prevState
)
}
componentWillUnmount() {
console.log('componentWillUnmount 在组件从 DOM 中移除的时候立刻被调用')
}
render() {
return <p>时间 => {this.state.date.toLocaleString()}</p>
}
}
打印截图
codepen
https://codepen.io/ducafecat/...
4.4 react 16.x
新版本变动
4.4.1 计划取消 3 个生命周期函数
分别是 componentWillMount
, componentWillReceiveProps
, componentWillUpdate
理由是在新的升级中,存在漏洞(在Facebook上,他们维护了超过50,000个React组件。 )
注意:
弃用警告将在未来的16.x版本中启用,但旧版生命周期将继续运行至17.x版。
即使在17.x版中,仍然可以使用它们,但它们会以『UNSAFE_』为前缀被重命名,以表明它们可能会引起问题。我们还准备了一个自动化的脚本来在现有代码中对它们重新命名。UNSAFE_componentWillMount()
UNSAFE_componentWillReceiveProps()
UNSAFE_componentWillUpdate()
4.4.2 新增 getDerivedStateFromProps
组件实例化后和接受新属性时将会调用
代码
// 组件
class ElementLifecycleNew extends Component {
constructor(props) {
super(props)
this.state = {}
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log(
'getDerivedStateFromProps 组件实例化后和接受新属性时将会调用',
nextProps,
prevState
)
// return null // 无需改变 返回 null
return {
date: new Date('2011-11-11 11:11:11')
}
}
render() {
return <p>{this.state.date.toLocaleString()}</p>
}
}
// 调用
<ElementLifecycleNew date={new Date('2009-09-09 09:09:09')} />
如果你不想改变状态 state
, 返回 null
4.4.3 新增 getSnapshotBeforeUpdate + componentDidUpdate
getSnapshotBeforeUpdate()
在最新的渲染输出提交给DOM前将会立即调用。它让你的组件能在当前的值可能要改变前获得它们。这一生命周期返回的任何值将会 作为参数被传递给componentDidUpdate()
。
// 代码
class ElementLifecycleNew2 extends Component {
listRef = React.createRef()
constructor(props) {
super(props)
this.state = {
date: props.date
}
}
componentDidMount() {
console.log('componentDidMount')
this.setState({date: new Date('2011-11-22 22:22:22')})
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('getSnapshotBeforeUpdate', prevProps, prevState, this.state)
return {
offset: 80
}
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('componentDidUpdate', snapshot)
this.listRef.current.style.top = `${snapshot.offset}px`
}
render() {
return (
<div
style={{
height: 200,
width: 150,
backgroundColor: 'blue',
position: 'relative',
color: '#fff'
}}
>
<p>{this.state.date.toLocaleString()}</p>
<div
ref={this.listRef}
style={{
height: 20,
width: 150,
backgroundColor: 'red',
top: 0,
position: 'absolute'
}}
/>
</div>
)
}
}
// 调用
这个例子的流程是:
1. `componentDidMount` 中修改了 state 触发 `getSnapshotBeforeUpdate`
2. `getSnapshotBeforeUpdate` 获取修改前的 属性、状态,已修改的 状态,然后一个修改值 `offset`
3. `componentDidUpdate` 中的 `snapshot` 获取修改值 ,直接 `ref` `dom` 修改 `style`
简单说就是不修改 state 更新机制,来维护 dom
,比如改改 宽 高 位置
5. 事件 Event
在 react 里使用事件,写法很多,这里采用官方推荐的方式
5.1 无参数传递
- 采用非箭头函数定义事件
handleChange(e) {
...
}
- 在构造函数时绑定事件
class InputView extends Component {
constructor(props) {
...
this.handleChange = this.handleChange.bind(this)
}
使用 bind(this)
方式
- 绑定事件
<input
...
onChange={this.handleChange}
/>
5.2 有参数传递
- 事件参数要放在最后
handleChangeVal(val, e) {
console.log(val)
this.setState({value: e.target.value})
}
- 绑定事件
<input
...
onChange={this.handleChangeVal.bind(this, '123')}
/>
5.3 例子
class InputView extends Component {
constructor(props) {
super(props)
this.state = {value: ''}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(e) {
this.setState({value: e.target.value})
}
handleChangeVal(val, e) {
console.log(val)
this.setState({value: e.target.value})
}
handleSubmit(e) {
e.preventDefault() // 阻止事件
console.log('handleSubmit')
}
render() {
return (
<form onSubmit={this.handleSubmit} style={{display: 'inline-flex'}}>
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
<input
type="text"
value={this.state.value}
onChange={this.handleChangeVal.bind(this, '123')}
/>
<input type="submit" value="提交" />
<p>{this.state.value}</p>
</form>
)
}
}
codepen
https://codepen.io/ducafecat/...
6. 样式 Style
有两种方式维护样式
6.1 import
样式文件
-
base.css
样式文件
.bg {
background-color: rgb(101, 40, 241);
color: white;
font-weight: 500;
}
-
.js
中引入
import './base.css'
6.2 直接编写 style
- 编写
style
对象
const styles = {}
styles.fill = {
position: 'relative',
height: '200px',
width: '500px'
}
...
- 传值使用
<div style={styles.fill}>...
- 混合使用
<div
style={{
...styles.fill,
...styles.hsl,
background: `hsl(${params.h}, ${params.s}%, ${params.l}%)`
}}
>
可以发现这样的写法,对编程控制样式还是很有用的
本文代码
参考
- Index as a key is an anti-pattern
- Introducing JSX
- Rendering Elements
- Components and Props
- State and Lifecycle
- Handling Events
- lists-and-keys.html
- React.Component
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。