目录
1. Hook
1.1 状态钩子 - State Hook
useState
1.2 副作用钩子 - Effffect Hook
原理
渲染即调用、一次调用、某种状态下调用
1.3 自定义钩子 - Custom Hook
多组件调用同一个
1.4 其他Hook
2. 组件跨层级通信 - Context
Context相关API
3种方法组件通信
3. 组件设计与实现
表单组件实现
内容
1. Hook
主要应用于函数组件,解决函数组件没有状态和生命周期的问题。
1.1 状态钩子 - State Hook
[state, setState] = useState(init),useState函数传入参数init,返回值是数组,数组内 [state状态, setState修改状态]
import React, { useState } from "react";
export default function HookTest() {
// useState(initState)
const [count, setCount] = useState(0);
// 多个状态
const age = useAge();
const [fruit, setFruit] = useState("banana");
const [input, setInput] = useState("");
const [fruits, setFruits] = useState(["apple", "banana"]);
return (
<div>
<p>点击了{count}次</p>
<button onClick={() => setCount(count + 1)}>点击</button>
<p>年龄:{age ? age : 'loading...'}</p>
<p>选择的水果:{fruit}</p>
<p>
<input
type="text"
value={input}
onChange={e => setInput(e.target.value)}
/>
<button onClick={() => setFruits([...fruits, input])}>新增水果</button>
</p>
<ul>
{fruits.map(f => (
<li key={f} onClick={() => setFruit(f)}>
{f}
</li>
))}
</ul>
</div>
);
}
1.2 副作用钩子 - Effffect Hook
原理:给函数组件增加操作副作用的能力。具有componentDidMounted、componentDidUpdate、componentDidDestroy三个生命周期相同的功能。
渲染即调用、一次调用()、某种状态下调用
useEffect(fn, []),每次渲染执行useEffect函数;第二个参数控制什么时候执行,如果是空数组,则执行一次,类似componentDidMounted;第二个参数[count, fill], 当只有在count或fill修改的情况下才执行函数useEffect。
import React, { useState, useEffect } from "react";
export default function HookTest() {
// useState(initState)
const [count, setCount] = useState(0);
// 副作用钩子会在每次渲染时都执行,每次count修改才会执行
useEffect(() => {
document.title = `您点击了${count}次`;
}, [count]);
// 如果仅打算执行一次,传递第二个参数为[]
// componentDidMount
useEffect(() => {
// api调用
console.log("api调用");
}, []);
return (
<div>
<p>点击了{count}次</p>
<button onClick={() => setCount(count + 1)}>点击</button>
</div>
);
}
1.3 自定义钩子 - Custom Hook
多组件调用同一个
import React, { useState, useEffect } from "react";
// 自定义hook是一个函数,名称用“use"开头,函数内部可以调用其他钩子
function useAge() {
const [age, setAge] = useState(0);
useEffect(() => {
setTimeout(() => {
setAge(20);
}, 2000);
});
return age;
}
export default function HookTest() {
// const [age] = useState("0");
const age = useAge();
return (
<div>
<p>年龄:{age ? age : 'loading...'}</p>
</div>
);
}
1.4 其他Hook
useContext、useReducer、useCallBack、useMemo
useCallBack、useMemo类似vue中的computed计算属性
2. 组件跨层级通信 - Context
2.1 Context相关API
React.createContext
Context.Provider
Context.Comsumer
Class.contentType
2.2 3种方法组件通信
2.2 3种方法组件通信
import React, { useContext } from "react";
// 1.创建上下文
const MyContext = React.createContext();
const { Provider, Consumer } = MyContext;
function Child(prop) {
return <div>Child: {prop.foo}</div>;
}
// 2.使用hook消费
function Child2() {
const context = useContext(MyContext);
return <div>Child2: {context.foo}</div>;
}
// 3. 使用class指定静态contextType
class Child3 extends React.Component {
// 设置静态属性通知编译器获取上下文中数据并赋值给this.context
static contextType = MyContext;
render() {
return <div>Child3: {this.context.foo}</div>
}
}
export default function ContextTest() {
return (
<div>
<Provider value={{ foo: "bar" }}>
{/* 消费方法1:Consumer */}
<Consumer>{value => <Child {...value} />}</Consumer>
{/* 消费方法2:hook */}
<Child2 />
{/* 消费方法3:contextType */}
<Child3 />
</Provider>
</div>
);
}
3. 组件设计与实现
表单组件实现
import React from "react"; import { Input, Button } from "antd"; // 创建一个高阶组件:扩展现有表单,事件处理、数据收集、校验 function kFormCreate(Comp) { return class extends React.Component { constructor(props) { super(props); this.options = {}; this.state = {}; } handleChange = e => { const { name, value } = e.target; console.log(name, value); this.setState({ [name]: value }, () => { // 确保值发生变化再校验 this.validateField(name); }); }; // 单项校验 validateField = field => { // 1. 获取校验规则 const rules = this.options[field].rules; // 任意一项失败则返回false const ret = !rules.some(rule => { if (rule.required) { if (!this.state[field]) { //校验失败 this.setState({ [field + "Message"]: rule.message }); return true; } } }); if (ret) { // 校验成功 this.setState({ [field + "Message"]: "" }); } return ret; }; // 校验所有字段 validate = cb => { const rets = Object.keys(this.options).map(field => this.validateField(field) ); const ret = rets.every(v => v == true); cb(ret, this.state); }; // 创建input包装器 getFieldDec = (field, option) => { // 保存当前输入项配置 this.options[field] = option; return InputComp => ( <div> {React.cloneElement(InputComp, { name: field, value: this.state[field] || "", onChange: this.handleChange })} {/* 校验错误信息 */} {this.state[field+'Message'] && ( <p style={{color:'red'}}>{this.state[field+'Message']}</p> )} </div> ); }; render() { return <Comp getFieldDec={this.getFieldDec} validate={this.validate} />; } }; } @kFormCreate class KForm extends React.Component { onSubmit = () => { console.log("submit"); // 校验所有项 this.props.validate((isValid, data) => { if (isValid) { //提交登录 console.log("登录:", data); // 后续登录逻辑 } else { alert("校验失败"); } }); }; render() { const { getFieldDec } = this.props; return ( <div> {getFieldDec("uname", { rules: [{ required: true, message: "用户名必填" }] })(<Input />)} {getFieldDec("pwd", { rules: [{ required: true, message: "密码必填" }] })(<Input type="password" />)} <Button onClick={this.onSubmit}>登录</Button> </div> ); } } export default KForm;