第六节:受控组件 、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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。