受控组件
- 在HTML中,表单元素(input,textarea,select)通常自己维护state,并根据用户输入进行更新
- 而在React中,可变状态通常保存在组件的state属性中,并且只能通过使用setState()来更新
- 将两者结合起来,使React的state成为“唯一数据源”
- 渲染表单的React组件还控制着用户输入过程中表单发生的操作
- 被React以这种方式控制取值的表单输入元素叫做“受控组件”
- 由于在表单元素上设置了value属性,因此显示的值将始终为this.state.value,这使得React的state成为唯一数据源
form提交的事件劫持,阻止默认行为
两个input的事件合并成一个函数(计算属性名[name])
checkbox的单选和多选绑定
select的单选和多选绑定
代码实现:
import React, { PureComponent, createRef } from "react";
export class App extends PureComponent {
constructor() {
super();
this.state = {
username: "",
password: "",
isAgree: false,
hobbies: [
{ value: "sing", text: "唱", isChecked: false },
{ value: "dance", text: "跳", isChecked: false },
{ value: "rap", text: "rap", isChecked: false },
],
fruit: ["origin"],
intro: "哈哈哈zm",
};
this.introRef = createRef();
}
handleSubmitClick(event) {
// 1.阻止默认的行为
event.preventDefault();
// 2.获取到所有的表单数据
console.log("获取所有的输入内容");
console.log("用户名 密码", this.state.username, this.state.password);
const hobbies = this.state.hobbies.filter((item) => item.isChecked).map((item) => item.value);
console.log("hobbies=> ", hobbies);
console.log("fruit=> ", this.state.fruit);
console.log("this.introRef.current.value=> ", this.introRef.current.value);
// 3.以网络请求方式,将数据传递给服务器(ajax/fetch/axios)
}
handleInputChange(event) {
this.setState({
[event.target.name]: event.target.value,
});
}
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 });
}
handleFruitChange(event) {
const options = Array.from(event.target.selectedOptions);
const values = options.map((item) => item.value);
this.setState({ fruit: values });
// Array.from(可迭代对象)
// Array.from(arguments)
// const value2 = Array.from(event.target.selectedOptions, (item) => item.value);
// console.log("value2=> ", value2);
}
render() {
const { username, password, isAgree, hobbies, fruit } = this.state;
return (
<div>
<form onSubmit={(e) => this.handleSubmitClick(e)}>
{/* 1.用户名和密码 */}
<div>
<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>
</div>
{/* 2.checkbox单选 */}
<label htmlFor="agree">
<input id="agree" type="checkbox" checked={isAgree} onChange={(e) => this.handleAgreeChange(e)} />
同意协议
</label>
{/* 3.checkbox多选 */}
<div>
zm的爱好:
{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>
{/* 4.select */}
<select value={fruit} onChange={(e) => this.handleFruitChange(e)} multiple>
<option value="apple">苹果</option>
<option value="origin">橘子</option>
<option value="banana">香蕉</option>
</select>
<button type="submit">注册</button>
</form>
{/* 非受控组件 */}
<div>
<input type="text" ref={this.introRef} defaultValue="default_username-zm" />
</div>
</div>
);
}
}
export default App;
非受控组件
非受控组件的使用
- ref绑定input
- 获取值,this.inputRef.current.value
- defaultValue默认值
{/* 非受控组件 */}
<div>
<input type="text" ref={this.introRef} defaultValue="default_username-zm" />
</div>
高阶组件-高阶函数
高阶组件定义
- 是一个函数
- 特定:接收一个组件作为参数,并且返回一个新的组件
- 本质:对传入的组件进行劫持
import React, { PureComponent } from "react";
// 定义一个高阶组件
function hoc(Cpn) {
// 1.定义类组件
class NewCpn extends PureComponent {
render() {
return <Cpn />;
}
}
return NewCpn;
// 定义函数组件
// function NewCpn2(props) {
// }
// return NewCpn2
}
class HelloZm extends PureComponent {
render() {
return <h1>hello zm</h1>;
}
}
const HelloZmHOC = hoc(HelloZm);
export class HocDefine extends PureComponent {
render() {
return (
<div>
HocDefine
<div>
<HelloZmHOC />
</div>
</div>
);
}
}
export default HocDefine;
应用一:增强props
- userInfo
import { PureComponent } from "react";
// 定义组件:给一些需要特殊数据的组件,注入props
function enhancedUserInfo(OriginCpn) {
return class extends PureComponent {
constructor() {
super();
this.state = {
userInfo: {
name: "zm",
age: 21,
},
};
}
render() {
return <OriginCpn {...this.props} {...this.state.userInfo} />;
}
};
}
export default enhancedUserInfo;
import React, { PureComponent } from "react";
import enhancedUserInfo from "./hoc/enhanced_props";
const Home = enhancedUserInfo(function (props) {
return (
<h1>
Home:{props.name}-{props.age}
</h1>
);
});
const Profile = enhancedUserInfo(function (props) {
return (
<h1>
Profile:{props.name}-{props.age}
</h1>
);
});
const HelloFriend = enhancedUserInfo(function (props) {
return (
<h1>
HelloFriend:{props.name}-{props.age}
</h1>
);
});
export class App extends PureComponent {
render() {
return (
<div>
<h2>props增强</h2>
<Home banners={["轮播图1", "轮播图2"]} />
<Profile />
<HelloFriend />
</div>
);
}
}
export default App;
- ThemeContext 高阶组件应用:Context共享
import React, { PureComponent } from "react";
import Cart from "./pages/Cart";
import Detail from "./pages/Detail";
import About from "./pages/About";
import ThemeContext from "./context/theme_context";
import Product from "./pages/Product";
export class App extends PureComponent {
constructor(props) {
super();
this.state = {
isLogin: false,
};
}
loginClick() {
localStorage.setItem("token", "wzmdhscw");
this.forceUpdate(); //强制更新
}
render() {
return (
<div>
<h2>App</h2>
<button onClick={(e) => this.loginClick()}>登录</button>
<Cart />
<Detail />
<About />
<ThemeContext.Provider value={{ color: "blue", size: 18 }}>
<Product />
</ThemeContext.Provider>
</div>
);
}
}
export default App;
theme_context.js
import { createContext } from "react";
const ThemeContext = createContext();
export default ThemeContext;
- withTheme()
import ThemeContext from "../context/theme_context";
function withTheme(OriginCpn) {
return (props) => {
return (
<ThemeContext.Consumer>
{(value) => {
return <OriginCpn {...value} {...props} />;
}}
</ThemeContext.Consumer>
);
};
}
export default withTheme;
import React, { PureComponent } from "react";
import withTheme from "../hoc/with_theme";
export class Product extends PureComponent {
render() {
return (
<div>
Product Page data from withTheme HOC:
{this.props.color}-{this.props.size}
</div>
);
}
}
export default withTheme(Product);
应用二:登录鉴权
function loginAuth(OriginCpn) {
return (props) => {
// 从localStorage中获取token
const token = localStorage.getItem("token");
if (token) {
return <OriginCpn {...props} />;
} else {
return <h2>请先登录,再进行跳转到对应的页面中</h2>;
}
};
}
export default loginAuth;
import React, { PureComponent } from "react";
import loginAuth from "../hoc/login_auth";
export class Cart extends PureComponent {
render() {
return <div>Cart Page</div>;
}
}
export default loginAuth(Cart);
import React, { PureComponent } from "react";
import Cart from "./pages/Cart";
export class App extends PureComponent {
constructor(props) {
super();
this.state = {
isLogin: false,
};
}
loginClick() {
localStorage.setItem("token", "wzmdhscw");
// this.setState({
// isLogin: true,
// });
this.forceUpdate(); //强制更新
}
render() {
// const { isLogin } = this.state;
return (
<div>
<h2>App</h2>
<button onClick={(e) => this.loginClick()}>登录</button>
<Cart />
</div>
);
}
}
export default App;
应用三:生命周期劫持
import { PureComponent } from "react";
function logRenderTime(OriginComponent) {
// return function () {};
return class extends PureComponent {
UNSAFE_componentWillMount() {
this.beginTime = new Date().getTime();
}
componentDidMount() {
this.endTime = new Date().getTime();
const interval = this.endTime - this.beginTime;
console.log(`当前页面花费了${interval}ms渲染完成!`);
}
render() {
return <OriginComponent {...this.props} />;
}
};
}
export default logRenderTime;
import React, { PureComponent } from "react";
import logRenderTime from "../hoc/log_render_time";
export class Detail extends PureComponent {
// UNSAFE_componentWillMount() {
// this.beginTime = new Date().getTime();
// }
// componentDidMount() {
// this.endTime = new Date().getTime();
// const interval = this.endTime - this.beginTime;
// console.log(`当前页面花费了${interval}ms渲染完成!`);
// }
render() {
return (
<div>
<h2>Detail</h2>
{/* ul>li{数据列表$}*10 */}
<ul>
<li>数据列表1</li>
<li>数据列表2</li>
<li>数据列表3</li>
<li>数据列表4</li>
<li>数据列表5</li>
<li>数据列表6</li>
<li>数据列表7</li>
<li>数据列表8</li>
<li>数据列表9</li>
<li>数据列表10</li>
</ul>
</div>
);
}
}
export default logRenderTime(Detail);
高阶组件的意义
- 代码复用(mixins -> HOC -> hooks)