受控组件和非受控组件,高阶组件及应用

52 阅读3分钟

受控组件

  • 在HTML中,表单元素(input,textarea,select)通常自己维护state,并根据用户输入进行更新
  • 而在React中,可变状态通常保存在组件的state属性中,并且只能通过使用setState()来更新
  • 将两者结合起来,使React的state成为“唯一数据源”
  • 渲染表单的React组件还控制着用户输入过程中表单发生的操作
  • 被React以这种方式控制取值的表单输入元素叫做“受控组件
  • 由于在表单元素上设置了value属性,因此显示的值将始终为this.state.value,这使得React的state成为唯一数据源

image.png

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;

非受控组件

image.png

非受控组件的使用

  • ref绑定input
  • 获取值,this.inputRef.current.value
  • defaultValue默认值
        {/* 非受控组件 */}
        <div>
          <input type="text" ref={this.introRef} defaultValue="default_username-zm" />
        </div>

高阶组件-高阶函数

image.png

高阶组件定义

  • 是一个函数
  • 特定:接收一个组件作为参数,并且返回一个新的组件
  • 本质:对传入的组件进行劫持
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;

应用三:生命周期劫持

image.png

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) image.png