zoukankan      html  css  js  c++  java
  • react基础---组件02

    目录
    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>
      );
    }
    View Code
    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>
      );
    }
    View Code
    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>
      );
    }
    View Code
    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种方法组件通信
    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>
      );
    }
    View Code
    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;
    View Code
  • 相关阅读:
    NodeJs 实现 WebSocket 即时通讯(版本二)
    (JavaScript) 时间转为几天前、几小时前、几分钟前
    (IDEA) 从数据库快速生成Spring Data JPA实体类
    (IDEA) 设置编码统一为UTF-8
    ElasticSearch 中文分词插件ik 的使用
    Logstash 安装及简单实用(同步MySql数据到Elasticsearch)(Windows)
    Docker在Linux上 基本使用
    vue设置页面标题
    HTML5实战与剖析之字符集属性(charset和defaultCharset)
    asp.net传多个值到其它页面的方法
  • 原文地址:https://www.cnblogs.com/shaokevin/p/12259789.html
Copyright © 2011-2022 走看看