第六节:受控组件 、portals、fragment、严格模式
一. 受控组件
1. 受控组件
(1) 在 HTML 中,表单元素(如<input>、 <textarea> 和 <select>)之类的表单元素通常自己维护 state,并根据用户输入进行更新。
(2) 而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。
我们将两者结合起来,使React的state成为 "唯一数据源";
渲染表单的 React 组件还控制着用户输入过程中表单发生的操作;
被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”;
(3) 由于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value,这使得 React 的 state 成为唯一数据源。
(4) 由于 handleUsernameChange 在每次按键时都会执行并更新 React 的 state,因此显示的值将随着用户输入而更新。
受控组件基本使用
import React, { PureComponent } from "react";
export class App extends PureComponent {
constructor() {
super();
this.state = {
username: "ypf",
};
}
inputChange(event) {
console.log("inputChange:", event.target.checked);
}
render() {
const { username } = this.state;
return (
<div>
{/* 受控组件 */}
<input type="checkbox" onChange={e => this.inputChange(e)} />
{/* 非受控组件 */}
<input type="text" />
<h2>username: {username}</h2>
</div>
);
}
}
export default App;
自己提交form表单
import React, { PureComponent } from "react";
export class App extends PureComponent {
constructor() {
super();
this.state = {
username: "",
};
}
handleSubmitClick(event) {
// 1.阻止默认的行为
event.preventDefault();
// 2.获取到所有的表单数据, 对数据进行组件
console.log("获取所有的输入内容:", this.state.username);
// 3.以网络请求的方式, 将数据传递给服务器(ajax/fetch/axios)
}
handleUsernameChange(event) {
this.setState({ username: event.target.value });
}
render() {
const { username } = this.state;
return (
<div>
<form onSubmit={e => this.handleSubmitClick(e)}>
{/* 1.用户名和密码 */}
<label htmlFor="username">
用户:
<input
id="username"
type="text"
name="username"
value={username}
onChange={e => this.handleUsernameChange(e)}
/>
</label>
<button type="submit">注册</button>
</form>
</div>
);
}
}
export default App;
多个input对同一个函数
import React, { PureComponent } from "react";
export class App extends PureComponent {
constructor() {
super();
this.state = {
username: "",
password: "",
};
}
handleSubmitClick(event) {
// 1.阻止默认的行为
event.preventDefault();
// 2.获取到所有的表单数据, 对数据进行组件
console.log("获取所有的输入内容:");
console.log(this.state.username, this.state.password);
// 3.以网络请求的方式, 将数据传递给服务器(ajax/fetch/axios)
}
// 方案1:一个input对应一个方法
// handleUsernameChange(event) {
// this.setState({ username: event.target.value })
// }
// handlePasswordChange(event) {
// this.setState({ password: event.target.value })
// }
// 方案2:动态设置属性名称,总共几个方法即可
handleInputChange(event) {
this.setState({
[event.target.name]: event.target.value,
});
}
render() {
const { username, password } = this.state;
return (
<div>
<form onSubmit={e => this.handleSubmitClick(e)}>
{/* 1.用户名和密码 */}
<label htmlFor="username">
用户:
<input
id="username"
type="text"
name="username"
value={username}
onChange={e => this.handleInputChange(e)}
/>
</label>
<label htmlFor="password">
密码:
<input
id="password"
type="password"
name="password"
value={password}
onChange={e => this.handleInputChange(e)}
/>
</label>
<button type="submit">注册</button>
</form>
</div>
);
}
}
export default App;
checkbox的单选多选
import React, { PureComponent } from "react";
export class App extends PureComponent {
constructor() {
super();
this.state = {
isAgree: false,
hobbies: [
{ value: "sing", text: "唱", isChecked: false },
{ value: "dance", text: "跳", isChecked: false },
{ value: "rap", text: "rap", isChecked: false },
],
};
}
handleSubmitClick(event) {
// 1.阻止默认的行为
event.preventDefault();
// 2.获取到所有的表单数据, 对数据进行组件
console.log("获取所有的输入内容--单选框:");
console.log(this.state.isAgree);
console.log("获取所有的输入内容--多选框:");
const hobbies = this.state.hobbies
.filter(item => item.isChecked)
.map(item => item.value);
console.log(hobbies);
}
// 单选框监听事件
handleAgreeChange(event) {
this.setState({ isAgree: event.target.checked });
}
// 多选框监听事件
handleHobbiesChange(event, index) {
const hobbies = [...this.state.hobbies];
hobbies[index].isChecked = event.target.checked;
this.setState({ hobbies });
}
render() {
const { isAgree, hobbies } = this.state;
return (
<div>
<form onSubmit={e => this.handleSubmitClick(e)}>
{/* 2.checkbox单选 */}
<label htmlFor="agree">
<input
id="agree"
type="checkbox"
checked={isAgree}
onChange={e => this.handleAgreeChange(e)}
/>
同意协议
</label>
{/* 3.checkbox多选 */}
<div>
您的爱好:
{hobbies.map((item, index) => {
return (
<label htmlFor={item.value} key={item.value}>
<input
type="checkbox"
id={item.value}
checked={item.isChecked}
onChange={e => this.handleHobbiesChange(e, index)}
/>
<span>{item.text}</span>
</label>
);
})}
</div>
<div>
<button type="submit">注册</button>
</div>
</form>
</div>
);
}
}
export default App;
select的单选多选
import React, { PureComponent } from "react";
export class App extends PureComponent {
constructor() {
super();
this.state = {
fruit: ["banana"], //用于设置默认选择
};
}
handleSubmitClick(event) {
// 1.阻止默认的行为
event.preventDefault();
// 2.获取到所有的表单数据, 对数据进行组件
console.log("获取所有的输入内容:", this.state.fruit);
}
handleFruitChange(event) {
// console.log(event.target.selectedOptions);
// 多选的写法
const options = Array.from(event.target.selectedOptions);
const values = options.map(item => item.value);
this.setState({ fruit: values });
// 额外补充: Array.from(可迭代对象) 写法2
// const values2 = Array.from(event.target.selectedOptions, item => item.value)
// console.log(values2)
// this.setState({ fruit: values2 })
}
render() {
const { fruit } = this.state;
return (
<div>
<form onSubmit={e => this.handleSubmitClick(e)}>
{/* 4.select--- 多选不好用 */}
<select
value={fruit}
onChange={e => this.handleFruitChange(e)}
multiple="multiple"
>
<option value="apple">苹果</option>
<option value="orange">橘子</option>
<option value="banana">香蕉</option>
</select>
<div>
<br />
<button type="submit">注册</button>
</div>
</form>
</div>
);
}
}
export default App;
2. 非受控组件
(1) React推荐大多数情况下使用 受控组件 来处理表单数据:
一个受控组件中,表单数据是由 React 组件来管理的;
另一种替代方案是使用非受控组件,这时表单数据将交由 DOM 节点来处理;
(2) 如果要使用非受控组件中的数据,那么我们需要使用 ref 来从DOM节点中获取表单数据。
我们来进行一个简单的演练:◼使用ref来获取input元素;◼ 在非受控组件中通常使用defaultValue来设置默认值;
(3)同样,<input type="checkbox"> 和 <input type="radio"> 支持 defaultChecked,<select> 和 <textarea> 支持 defaultValue。
import React, { createRef, PureComponent } from "react";
export class App extends PureComponent {
constructor() {
super();
this.state = {
intro: "哈哈哈",
};
this.introRef = createRef();
}
componentDidMount() {
// this.introRef.current.addEventListener
}
handleSubmitClick(event) {
// 1.阻止默认的行为
event.preventDefault();
// 2.获取到所有的表单数据, 对数据进行组件
console.log("获取结果:", this.introRef.current.value);
}
render() {
const { intro } = this.state;
return (
<div>
<form onSubmit={e => this.handleSubmitClick(e)}>
{/* 5.非受控组件 */}
<input type="text" defaultValue={intro} ref={this.introRef} />
<div>
<button type="submit">注册</button>
</div>
</form>
</div>
);
}
}
export default App;
二. portals
1. 背景
某些情况下,我们希望渲染的内容独立于父组件,甚至是独立于当前挂载到的DOM元素中(默认都是挂载到public/index.html中的id为root的DOM元素上的)
2. 解决方案
(1).Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案: createPortal(child, container)
A. 参数1:(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment;
B. 参数2:(container)是一个 DOM 元素;
(2). 另外需要在 publich/index.html 中增加一个承载容器:
如: <div id="ypf"></div>
export class App extends PureComponent {
render() {
return (
<div>
<h2>App</h2>
{createPortal(<h4>hello h4</h4>, document.querySelector("#ypf"))}
</div>
);
}
}

三. fragment
1. 背景
在之前的开发中,我们总是在一个组件中返回内容时包裹一个div元素,如果想不被包裹,则 Fragment 允许你将子列表分组,而无需向 DOM 添加额外节点
注:Fragment 并没有额外生成dom元素
2. 实操
(1) 基本用法
直接使用<Fragment></Fragment>包裹即可
(2) 语法糖简写
使用 空标签 <> </> 包裹即可
(3) 注意事项
如果绑定 如果要绑定key,Fragment不能省略,不能用语法糖
export class App extends PureComponent {
render() {
const mySections = [
{ title: "哈哈哈", content: "我是内容1" },
{ title: "呵呵呵", content: "我是内容2" },
{ title: "嘿嘿嘿", content: "我是内容3" },
{ title: "嘻嘻嘻", content: "我是内容4" },
];
// 写法1:基本用法
/*
return (
<Fragment>
<h2> hello h2 </h2>
<h3> hello h3</h3>
</Fragment>
);
*/
// 写法2:语法糖
/*
return (
<>
<h2> hello h2 </h2>
<h3> hello h3</h3>
</>
);
*/
// 写法3:如果需要绑定key,不能使用语法糖,即不能省略Fragment,
return (
<div>
{mySections.map(item => {
return (
<Fragment key={item.title}>
<p>{item.title}</p>
<p>{item.content}</p>
</Fragment>
);
})}
</div>
);
}
}
四. 严格模式
1. 说明
StrictMode 是一个用来突出显示应用程序中潜在问题的工具:
(1). 与 Fragment 一样,StrictMode 不会渲染任何可见的 UI;
(2). 它为其后代元素触发额外的检查和警告;
(3). 严格模式检查仅在开发模式下运行; 它们不会影响生产构建
2. 实操
可以局部开启,也可以全局开启严格模式,只需要用<StrictMode>包裹即可,如果全局开启,则在index.js 包裹即可
root.render(
<StrictMode>
<App />
</StrictMode>
);
如下案例:Home组件开启严格模式,Profile组件不开启
export class App extends PureComponent {
render() {
return (
<div>
{/* Home组件启用严格模式 */}
<StrictMode>
<Home />
</StrictMode>
{/* Profile组件不开启严格模式 */}
<Profile />
</div>
);
}
}
3. 严格模式检测了什么?
(1).识别不安全的生命周期:
(2).使用过时的ref API
(3).检查意外的副作用
A 这个组件的constructor会被调用两次;
B 这是严格模式下故意进行的操作,让你来查看在这里写的一些逻辑代码被调用多次时,是否会产生一些副作用;
C 在生产环境中,是不会被调用两次的;
(4).使用废弃的findDOMNode方法
在之前的React API中,可以通过findDOMNode来获取DOM,不过已经不推荐使用了,可以自行学习演练一下
(5).检测过时的context API
A. 早期的Context是通过static属性声明Context对象属性,通过getChildContext返回Context对象等方式来使用Context的;
B. 目前这种方式已经不推荐使用,大家可以自行学习了解一下它的用法;
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。

浙公网安备 33010602011771号