第六节:受控组件 、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;
View Code

自己提交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;
View Code

多个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;
View Code

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;
View Code

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;
View Code

 

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;
View Code

 

 

二. 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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
posted @ 2023-05-08 16:03  Yaopengfei  阅读(21)  评论(1编辑  收藏  举报