九、条件渲染
在 React 中,你可以创建不同的组件来封装各种你需要的行为。然后还可以根据应用的状态变化只渲染其中的一部分。
React 中的条件渲染和 JavaScript 中的一致,使用 JavaScript 操作符 if 或条件运算符来创建表示当前状态的元素,然后让 React 根据它们来更新 UI。
9.1 &&
你可以通过用花括号包裹代码在 JSX 中嵌入任何表达式 ,也包括 JavaScript 的逻辑与 &&,它可以方便地条件渲染一个元素。
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
// import App from './01_props/01_Parent_Child' // 父子组件
// import App from './01_props/02_Parent_Child_value' // 父组件给子组件传值
// import App from './01_props/03_Parent_Child_default' // 父组件给子组件传值,子组件设置默认值
// import App from './01_props/04_Parent-Child_type' // 父组件给子组件传值,子组件设置默认值,验证数据类型
// import App from './01_props/05_App_props_children' // 类插槽
// import App from './01_props/06_App_mutiple_props_children' // 类具名插槽 多个插槽
// import App from './01_props/07_App_mouse_tracker' // 鼠标跟随
// import App from './01_props/08_App_render_props' // 渲染属性 - 其他组件共享状态
// import App from './02_state/01_App_state_es6' // 初始化状态 - es6的构造函数
// import App from './02_state/02_App_state_es7' // 初始化状态 - es7 属性初始化器
// import App from './02_state/03_App_setState_function' // 修改状态 传递函数
// import App from './02_state/04_App_setState_object' // 修改状态 传递对象
// import App from './02_state/05_App_computed' // 类组件实现类似于vue的计算属性
// import App from './02_state/06_App_lifeCycle' // 生命周期
// import App from './03_event/01_App_event_es5' // es5事件绑定
// import App from './03_event/02_App_event_es6' // es6事件绑定
import App from './04_condition/01_App_condition_yu' // 条件判断与
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/04_condition/01_App_condition_yu.jsx
// src/04_condition/01_App_condition_yu.jsx
import React, { Component } from 'react';
class MailBox extends Component {
render () {
return (
<div>
{
this.props.unReadMessage.length > 0 && <div>还有{this.props.unReadMessage.length}未读邮件</div>
}
</div>
)
}
}
export default class App extends Component {
state = {
messageList: ['a', 'b', 'c', 'd']
}
render() {
return (
<div>
{
// 如果messageList从服务器获取, 初始数据为空数组,其实没有必要 map 遍历
this.state.messageList && this.state.messageList.map((item, index) => {
return (
<p key={ index }>
{ item }
<button onClick={
() => {
// 获取数据
const arr = this.state.messageList
// 处理数据
arr.splice(index, 1)
// 修改状态
this.setState({
messageList: arr
})
}
}>阅读</button>
</p>
)
})
}
<MailBox unReadMessage = { this.state.messageList }/>
</div>
);
}
}
9.2 三元运算符
条件渲染的另一种方法是使用 JavaScript 的条件运算符:
condition ? true : false。
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
// import App from './01_props/01_Parent_Child' // 父子组件
// import App from './01_props/02_Parent_Child_value' // 父组件给子组件传值
// import App from './01_props/03_Parent_Child_default' // 父组件给子组件传值,子组件设置默认值
// import App from './01_props/04_Parent-Child_type' // 父组件给子组件传值,子组件设置默认值,验证数据类型
// import App from './01_props/05_App_props_children' // 类插槽
// import App from './01_props/06_App_mutiple_props_children' // 类具名插槽 多个插槽
// import App from './01_props/07_App_mouse_tracker' // 鼠标跟随
// import App from './01_props/08_App_render_props' // 渲染属性 - 其他组件共享状态
// import App from './02_state/01_App_state_es6' // 初始化状态 - es6的构造函数
// import App from './02_state/02_App_state_es7' // 初始化状态 - es7 属性初始化器
// import App from './02_state/03_App_setState_function' // 修改状态 传递函数
// import App from './02_state/04_App_setState_object' // 修改状态 传递对象
// import App from './02_state/05_App_computed' // 类组件实现类似于vue的计算属性
// import App from './02_state/06_App_lifeCycle' // 生命周期
// import App from './03_event/01_App_event_es5' // es5事件绑定
// import App from './03_event/02_App_event_es6' // es6事件绑定
// import App from './04_condition/01_App_condition_yu' // 条件判断与
import App from './04_condition/02_App_condition_san' // 条件判断 三元运算符
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/04_condition/02_App_condition_san.jsx
// src/04_condition/02_App_condition_san.jsx
// 在jsx 中不要使用 if 以及 for 等流程控制语句
// 可以在 if 条件语句中 返回 jsx代码
import React, { Component } from 'react';
export default class App extends Component {
state = {
loginState: false
}
// 1.三元运算符
// render () {
// return (
// <div>
// { this.state.loginState ? '登录了' : '未登录' }
// <button onClick={ () => {
// this.setState({
// loginState: !this.state.loginState
// })
// }}>切换</button>
// </div>
// )
// }
// render () {
// if (this.state.loginState) {
// return (
// <div>
// 登录了
// <button onClick={ () => {
// this.setState({
// loginState: !this.state.loginState
// })
// }}>切换</button>
// </div>
// )
// } else {
// return (
// <div>
// 未登录
// <button onClick={ () => {
// this.setState({
// loginState: !this.state.loginState
// })
// }}>切换</button>
// </div>
// )
// }
// }
render () {
const HTML = <button onClick={ () => {
this.setState({
loginState: !this.state.loginState
})
}}>切换</button>
if (this.state.loginState) {
return (
<div>
登录了 { HTML }
</div>
)
} else {
return (
<div>
未登录 { HTML }
</div>
)
}
}
}
9.3 动态className
Vue中有很方便的动态绑定class属性的方式,v-bind:class,那么react怎么实现这样的效果呢?
<button class="btn btn-success btn-sm"></button>
<button class="btn btn-danger btn-sm"></button>
<button class="btn btn-warning btn-sm"></button>
{ this.state.type === 'success' ? 'btn btn-success btn-sm' : 'btn btn-sm'}
通过classnames这个插件可以实现
$ cnpm i classnames -S
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
// import App from './01_props/01_Parent_Child' // 父子组件
// import App from './01_props/02_Parent_Child_value' // 父组件给子组件传值
// import App from './01_props/03_Parent_Child_default' // 父组件给子组件传值,子组件设置默认值
// import App from './01_props/04_Parent-Child_type' // 父组件给子组件传值,子组件设置默认值,验证数据类型
// import App from './01_props/05_App_props_children' // 类插槽
// import App from './01_props/06_App_mutiple_props_children' // 类具名插槽 多个插槽
// import App from './01_props/07_App_mouse_tracker' // 鼠标跟随
// import App from './01_props/08_App_render_props' // 渲染属性 - 其他组件共享状态
// import App from './02_state/01_App_state_es6' // 初始化状态 - es6的构造函数
// import App from './02_state/02_App_state_es7' // 初始化状态 - es7 属性初始化器
// import App from './02_state/03_App_setState_function' // 修改状态 传递函数
// import App from './02_state/04_App_setState_object' // 修改状态 传递对象
// import App from './02_state/05_App_computed' // 类组件实现类似于vue的计算属性
// import App from './02_state/06_App_lifeCycle' // 生命周期
// import App from './03_event/01_App_event_es5' // es5事件绑定
// import App from './03_event/02_App_event_es6' // es6事件绑定
// import App from './04_condition/01_App_condition_yu' // 条件判断与
// import App from './04_condition/02_App_condition_san' // 条件判断 三元运算符
import App from './04_condition/03_App_condition_classname' // 条件判断 样式
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/04_condition/03_App_condition_classname.jsx
// src/04_condition/03_App_condition_classname.jsx
import React, { Component } from 'react'
import classnames from 'classnames'
export default class App extends Component {
state = {
type: 'default', // success danger warning default
size: 'md' // sm md lg (小 中 大)
}
render() {
return (
<div>
<button onClick={ () => this.setState({ type: 'success' })}>success</button>
<button onClick={ () => this.setState({ type: 'danger' })}>danger</button>
<button onClick={ () => this.setState({ type: 'warning' })}>warning</button>
<button onClick={ () => this.setState({ type: 'default' })}>default</button>
<button onClick={ () => this.setState({ size: 'sm' })}>sm</button>
<button onClick={ () => this.setState({ size: 'md' })}>md</button>
<button onClick={ () => this.setState({ size: 'lg' })}>lg</button>
<button className={
classnames({
btn: true,
'btn-success': this.state.type === 'success',
'btn-sm': this.state.size === 'sm'
})
}>success小号按钮</button>
<button className={ (this.state.type === 'success' && this.state.size === 'sm') ? 'btn btn-success btn-sm' : (this.state.type === 'success' && this.state.size !== 'sm') ? 'btn btn-success' : (this.state.type !== 'success' && this.state.size === 'sm') ? 'btn btn-sm' : 'btn'}>success小号按钮</button>
</div>
)
}
}
补充:
css-in-js
$ cnpm i styled-components -S
src/index.js
// src/index.js import React from 'react' import ReactDOM from 'react-dom/client' // 引入react组件时,后缀名可以不写 // import App from './01_props/01_Parent_Child' // 父子组件 // import App from './01_props/02_Parent_Child_value' // 父组件给子组件传值 // import App from './01_props/03_Parent_Child_default' // 父组件给子组件传值,子组件设置默认值 // import App from './01_props/04_Parent-Child_type' // 父组件给子组件传值,子组件设置默认值,验证数据类型 // import App from './01_props/05_App_props_children' // 类插槽 // import App from './01_props/06_App_mutiple_props_children' // 类具名插槽 多个插槽 // import App from './01_props/07_App_mouse_tracker' // 鼠标跟随 // import App from './01_props/08_App_render_props' // 渲染属性 - 其他组件共享状态 // import App from './02_state/01_App_state_es6' // 初始化状态 - es6的构造函数 // import App from './02_state/02_App_state_es7' // 初始化状态 - es7 属性初始化器 // import App from './02_state/03_App_setState_function' // 修改状态 传递函数 // import App from './02_state/04_App_setState_object' // 修改状态 传递对象 // import App from './02_state/05_App_computed' // 类组件实现类似于vue的计算属性 // import App from './02_state/06_App_lifeCycle' // 生命周期 // import App from './03_event/01_App_event_es5' // es5事件绑定 // import App from './03_event/02_App_event_es6' // es6事件绑定 // import App from './04_condition/01_App_condition_yu' // 条件判断与 // import App from './04_condition/02_App_condition_san' // 条件判断 三元运算符 // import App from './04_condition/03_App_condition_classname' // 条件判断 样式 import App from './04_condition/04_App_condition_cssinjs' // css in js const root = ReactDOM.createRoot(document.querySelector('#root')) root.render(<App />)
src/04_condition/04_App_condition_cssinjs.jsx
// src/04_condition/04_App_condition_cssinjs.jsx import styled from 'styled-components' import React, { Component } from 'react'; // css in js const Button = styled.button` padding: 30px; font-size: 20px; ` export default class App extends Component { render() { return ( <div> <Button>按钮</Button> </div> ); } }
- 模块化css
可以解决类似于 vue中 scoped
src/index.js
// src/index.js import React from 'react' import ReactDOM from 'react-dom/client' // 引入react组件时,后缀名可以不写 // import App from './01_props/01_Parent_Child' // 父子组件 // import App from './01_props/02_Parent_Child_value' // 父组件给子组件传值 // import App from './01_props/03_Parent_Child_default' // 父组件给子组件传值,子组件设置默认值 // import App from './01_props/04_Parent-Child_type' // 父组件给子组件传值,子组件设置默认值,验证数据类型 // import App from './01_props/05_App_props_children' // 类插槽 // import App from './01_props/06_App_mutiple_props_children' // 类具名插槽 多个插槽 // import App from './01_props/07_App_mouse_tracker' // 鼠标跟随 // import App from './01_props/08_App_render_props' // 渲染属性 - 其他组件共享状态 // import App from './02_state/01_App_state_es6' // 初始化状态 - es6的构造函数 // import App from './02_state/02_App_state_es7' // 初始化状态 - es7 属性初始化器 // import App from './02_state/03_App_setState_function' // 修改状态 传递函数 // import App from './02_state/04_App_setState_object' // 修改状态 传递对象 // import App from './02_state/05_App_computed' // 类组件实现类似于vue的计算属性 // import App from './02_state/06_App_lifeCycle' // 生命周期 // import App from './03_event/01_App_event_es5' // es5事件绑定 // import App from './03_event/02_App_event_es6' // es6事件绑定 // import App from './04_condition/01_App_condition_yu' // 条件判断与 // import App from './04_condition/02_App_condition_san' // 条件判断 三元运算符 // import App from './04_condition/03_App_condition_classname' // 条件判断 样式 // import App from './04_condition/04_App_condition_cssinjs' // css in js import App from './04_condition/05_App_module_css' // 模块化css const root = ReactDOM.createRoot(document.querySelector('#root')) root.render(<App />)
src/04_condition/05_App_module_css.jsx
// src/04_condition/05_App_module_css.jsx import './style.css' // 模块化css import style from './style.module.css' import React, { Component } from 'react'; export default class App extends Component { render() { return ( // <div className="container"> // <header className="header"></header> // <div className="content"></div> // <footer className="footer"></footer> // </div> <div className={ style.container }> <header className={ style.header}></header> <div className={ style.content}></div> <footer className={ style.footer}></footer> </div> ); } }
src/04_condition/style.module.css
* {padding: 0; margin: 0;} html, body{ height: 100%;} #root { height: 1000px;} .container { width: 100%; height: 600px; display: flex; flex-direction: column; } .header { height: 100px; background-color: #f66; } .content { flex: 1; } .footer { height: 100px; background-color: #ccc; }
9.4 动态style
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
// import App from './01_props/01_Parent_Child' // 父子组件
// import App from './01_props/02_Parent_Child_value' // 父组件给子组件传值
// import App from './01_props/03_Parent_Child_default' // 父组件给子组件传值,子组件设置默认值
// import App from './01_props/04_Parent-Child_type' // 父组件给子组件传值,子组件设置默认值,验证数据类型
// import App from './01_props/05_App_props_children' // 类插槽
// import App from './01_props/06_App_mutiple_props_children' // 类具名插槽 多个插槽
// import App from './01_props/07_App_mouse_tracker' // 鼠标跟随
// import App from './01_props/08_App_render_props' // 渲染属性 - 其他组件共享状态
// import App from './02_state/01_App_state_es6' // 初始化状态 - es6的构造函数
// import App from './02_state/02_App_state_es7' // 初始化状态 - es7 属性初始化器
// import App from './02_state/03_App_setState_function' // 修改状态 传递函数
// import App from './02_state/04_App_setState_object' // 修改状态 传递对象
// import App from './02_state/05_App_computed' // 类组件实现类似于vue的计算属性
// import App from './02_state/06_App_lifeCycle' // 生命周期
// import App from './03_event/01_App_event_es5' // es5事件绑定
// import App from './03_event/02_App_event_es6' // es6事件绑定
// import App from './04_condition/01_App_condition_yu' // 条件判断与
// import App from './04_condition/02_App_condition_san' // 条件判断 三元运算符
// import App from './04_condition/03_App_condition_classname' // 条件判断 样式
// import App from './04_condition/04_App_condition_cssinjs' // css in js
// import App from './04_condition/05_App_module_css' // 模块化css
import App from './04_condition/06_App_style' // 动态style
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/04_condition/06_App_style.jsx
// src/04_condition/06_App_style.jsx
import React, { Component } from 'react'
// style 小驼峰式 单位可省略
export default class App extends Component {
state = {
size: 12,
color: '#000'
}
render() {
// <></> 空标签
return (
<>
<input type="color" value={ this.state.color } onChange = { event => this.setState({ color: event.target.value })} />
<button onClick={ () => this.setState({ size: this.state.size + 2 })}>加</button>
<button onClick={ () => this.setState({ size: this.state.size - 2 })}>减</button>
<div style={ { fontSize: this.state.size + 'px', color: this.state.color } }>
自从去年中国决定逐渐放松防疫限制之后,中国疫情成为全球关注的焦点,引起诸多外国媒体的好奇。近日,《纽约时报》发布报道称:“早有预见中国的新冠清零会结束,但是从来没有想过会以这样的方式结束。”
</div>
</>
)
}
}
十、列表渲染
- map()方法、key
使用 map() 方法遍历数组
组件接收数组参数,每个列表元素分配一个 key,不然会出现警告 a key should be provided for list items,意思就是需要包含 key:
Keys 可以在 DOM 中的某些元素被增加或删除的时候帮助 React 识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。
一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用来自数据的id
作为元素的 key
当元素没有确定的 id 时,你可以使用他的序列号索引 index
作为 key
如果列表可以重新排序,我们不建议使用索引来进行排序,因为这会导致渲染变得很慢。
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
import App from './05_list/01_App_map'
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/05_list/01_App_map.jsx
// src/05_list/01_App_map.jsx
// React中使用 map 来遍历数据,需要 添加唯一值key
import React, { Component } from 'react'
export default class App extends Component {
state = {
list: ['aa', 'bb', 'cc', 'dd']
}
// 边遍历边渲染
// render() {
// return (
// <div>
// {
// this.state.list && this.state.list.map((item, index) => {
// return (
// <div key = { index }>{ item }</div>
// )
// })
// }
// </div>
// )
// }
// 先遍历后渲染
render () {
const arr = []
this.state.list.forEach((item, index) => {
arr.push(<div key = { index }>{ item }</div>)
})
return (
<div>
{ arr }
</div>
)
}
}
接口
http://121.89.205.189:3000/api/city/sortCity
实现多层遍历
src/index.js
// src/index.js import React from 'react' import ReactDOM from 'react-dom/client' // 引入react组件时,后缀名可以不写 // import App from './05_list/01_App_map' import App from './05_list/02_App_mutiple_map' const root = ReactDOM.createRoot(document.querySelector('#root')) root.render(<App />)
src/05_list/02_App_mutiple_map.jsx
// src/05_list/02_App_mutiple_map.jsx // React中使用 map 来遍历数据,需要 添加唯一值key import React, { Component } from 'react' export default class App extends Component { state = { list: ['aa', 'bb', 'cc', 'dd'], newList: [ { brand: '宝马', data: ['X5', 'X7'] }, { brand: '奥迪', data: ['Q3', 'RS7'] } ] } // 边遍历边渲染 // render() { // return ( // <div> // { // this.state.list && this.state.list.map((item, index) => { // return ( // <div key = { index }>{ item }</div> // ) // }) // } // { // this.state.newList && this.state.newList.map(item => { // return ( // <div key = { item.brand }> // { // item.brand // } // { // item.data.map((itm, idx) => { // return <p key = { idx }> { itm }</p> // }) // } // </div> // ) // }) // } // </div> // ) // } // 先遍历后渲染 render () { const arr = [] this.state.list.forEach((item, index) => { arr.push(<div key = { index }>{ item }</div>) }) const newArr = [] this.state.newList.forEach(item => { const newData = [] item.data.forEach((itm,idx) => { newData.push(<p key = { idx }>{ itm }</p>) }) newArr.push(<div key = { item.brand }>{ item.brand } - { newData }</div>) }) return ( <div> { arr } { newArr } </div> ) } }
十一、表单绑定
在 React 里,HTML 表单元素的工作方式和其他的 DOM 元素有些不同,这是因为表单元素通常会保持一些内部的 state。例如这个纯 HTML 表单只接受一个名称:
<form>
<label>
名字:
<input type="text" name="name" />
</label>
<input type="submit" value="提交" />
</form>
此表单具有默认的 HTML 表单行为,即在用户提交表单后浏览到新页面。如果你在 React 中执行相同的代码,它依然有效。但大多数情况下,使用 JavaScript 函数可以很方便的处理表单的提交, 同时还可以访问用户填写的表单数据。实现这种效果的标准方式是使用“受控组件”。
表单元素的value值受 state的控制
11.1 各种表单的绑定与取值
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
import App from './06_form/01_App_form'
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/06_form/01_App_form.jsx
// src/06_form/01_App_form.jsx
import React, { Component } from 'react';
export default class App extends Component {
state = {
userName: '',
password: '',
sex: '',
hobby: [],
lesson: '',
note: '',
flag: false
}
changeVal (event) {
console.log(event)
// this.setState({
// userName: event.target.value
// })
this.setState({
[event.target.name]: event.target.value
})
}
handlerHobbyChange (event) {
console.log(event.target.checked)
const checked = event.target.checked
const value = event.target.value
const arr = this.state.hobby
if (checked) {
arr.push(value)
} else {
const index = arr.findIndex((item) => {
return item === value
})
arr.splice(index, 1)
}
console.log('22', arr)
this.setState({
hobby: arr
})
}
render() {
return (
<div>
<div>
<input type="text" name="userName" value = { this.state.userName } onChange = { this.changeVal.bind(this) }/>
{ this.state.userName }
</div>
<div>
<input type="password" name="password" value = { this.state.password } onChange = { this.changeVal.bind(this) }/>
{ this.state.password }
</div>
<div>
<input type="radio" name="sex" value = '男' onChange={ this.changeVal.bind(this) }/>男
<input type="radio" name="sex" value = '女' onChange={ this.changeVal.bind(this) }/>女
---- { this.state.sex }
</div>
<div>
<input type="checkbox" name="hobby" value="🏀" onChange={ this.handlerHobbyChange.bind(this) }/>🏀
<input type="checkbox" name="hobby" value="⚽" onChange={ this.handlerHobbyChange.bind(this) }/>⚽
<input type="checkbox" name="hobby" value="🏐" onChange={ this.handlerHobbyChange.bind(this) }/>🏐
<input type="checkbox" name="hobby" value="🏓" onChange={ this.handlerHobbyChange.bind(this) }/>🏓
---
{
this.state.hobby && this.state.hobby.map(item => {
return <span key = { item }>{item}</span>
})
}
</div>
<div>
<select name="lesson" value = { this.state.lesson } onChange = { this.changeVal.bind(this) }>
<option value="一阶段">一阶段</option>
<option value="二阶段">二阶段</option>
<option value="三阶段">三阶段</option>
</select>
---- { this.state.lesson }
</div>
<div >
<textarea name="note" value={this.state.note} onChange = { this.changeVal.bind(this) }/>{ this.state.note }
</div>
<div >
<input type="checkbox" name="flag" value={ this.state.flag } onChange= { (event) => {
this.setState({
flag: event.target.checked
})
}} /> 同意 **** 协议 { this.state.flag }
</div>
<button onClick={() => {
console.log({
userName: this.state.userName,
password: this.state.password,
sex: this.state.sex,
hobby: this.state.hobby,
lesson: this.state.lesson,
note: this.state.note,
flag: this.state.flag
})
}}>获取数据</button>
</div>
);
}
}
11.2 受控表单以及受控组件
在 HTML 中,表单元素(如<input>
、 <textarea>
和 <select>
)之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()
来更新。
我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
input
、textarea
、select
受控组件: value的属性受了state
的控制
- 使用了受控组件,一定要写
value
属性以及onChange
事件
radio
、‘checkbox’ 受控组件: checked 的属性受了state
的控制如果需要设置默认值,那么需要通过
defaultValue
以及defaultChecked
设置
案例如上
十二、状态提升
在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state。这就是所谓的“状态提升”。
12.1 父子组件通信
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
import App from './07_state_up/01_App-parent-child-value'
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/07_state_up/01_App-parent-child-value.jsx
// src/07_state_up/07_state_up/01_App-parent-child-value.jsx
import React, { Component } from 'react'
class Child1 extends Component {
state = { count: 1 }
render () {
return (
<>
<h1>child1组件</h1>
{ this.state.count }
<button onClick={ () => { this.setState({count: this.state.count + 1} ) }}>加1</button>
</>
)
}
}
class Child2 extends Component {
state = { count: 1 }
render () {
return (
<>
<h1>child2组件</h1>
{ this.state.count }
<button onClick={ () => { this.setState({count: this.state.count + 1} ) }}>加1</button>
</>
)
}
}
class App extends Component {
render() {
return (
<div>
<Child1 ></Child1>
<hr></hr>
<Child2 ></Child2>
</div>
);
}
}
export default App;
我们发现Child1和Child2都是两个独立的个体,并没有实现数据共享
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
// import App from './07_state_up/01_App-parent-child-value'
import App from './07_state_up/02_App_state_up'
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/07_state_up/02_App_state_up.jsx
// src/07_state_up/07_state_up/02_App_state_up.jsx
import React, { Component } from 'react'
class Child1 extends Component {
// state = { count: 1 }
render () {
return (
<>
<h1>child1组件</h1>
{ this.props.count }
<button onClick={ () => { this.props.onClick() }}>加1</button>
</>
)
}
}
class Child2 extends Component {
// state = { count: 1 }
render () {
return (
<>
<h1>child2组件</h1>
{ this.props.count }
<button onClick={ () => { this.props.onClick() }}>加1</button>
</>
)
}
}
class App extends Component {
state = {
count: 1
}
add = () => {
this.setState({ count: this.state.count + 1 })
}
render() {
return (
<div>
<Child1 count = { this.state.count } onClick = { this.add }></Child1>
<hr></hr>
<Child2 count = { this.state.count } onClick = { this.add }></Child2>
</div>
);
}
}
export default App;
12.2 状态提升解读
实现方式是 利用最近的共同的父级组件中,用props
的方式传过去到两个子组件,props
中传的是一个setState
的方法,通过子组件触发props
传过去的方法,进而调用父级组件的setState
的方法,改变了父级组件的state
,调用父级组件的add
方法,进而同时改变了两个子级组件的数据
。
这是 两个有关连的同级组件的传值,因为react
的单项数据流,所以不在两个组件中进行传值,而是提升到 最近的共同的父级组件中,改变父级的state
,进而影响了两个子级组件的render
。
注意如果两个组件是同级组件(这两个组件的父组件是同一个)才考虑状态提升共享数据