美文网首页
react的新特性(二)---hooks

react的新特性(二)---hooks

作者: Mr无愧于心 | 来源:发表于2019-06-14 09:54 被阅读0次

产生背景

  • Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
  • react hooks 要解决的问题是状态共享,(自定义hook)是继render-propsHOC组件之后的第三种状态共享方案,不会产生jsx嵌套地狱的问题。
  • Hook 使你在无需修改组件结构的情况下复用状态逻辑。

状态共享的方式

  1. render-props方式
    render prop就是给组件添加一个值为函数的属性,这个函数可以在组件渲染(fn)的时候调用
class Mouse extends React.Component{
  constructor(props){
    super(props);
    this.state = { x: 0, y: 0 };
  }
  handleMouseMove=(event)=>{
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }
  render() {
    return (
      <div onMouseMove={this.handleMouseMove}>
        <p>The current mouse position is ({this.state.x}, {this.state.y})</p>
        {this.props.fn(this.state)} // 这个比较关键 fn 就是这个函数的属性
      </div>
    );
  }
}
//另一个组件cat,需要拿到mouse的数据
class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse;
    return (
      <img 
         src="/cat.jpg" 
         style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
    );
  }
}
//组合这两个组件
class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>Move the mouse around!</h1>
        <Mouse fn={mouse => (//将Mouse组件中的this.state拿到传递给cat组件
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}
//相当于cat组件使用了mouse组件,以后再有的dog,pig组件都可以使用mouse组件(实现mouse的重用);
  1. HOC组件(高阶组件是参数为组件,返回值为新组件的函数)
//每个经过高阶组件处理过的组件都会复用高阶组件里边的所有逻辑,
//但原则是高阶组件是一个纯函数,不会修改传入的组件
function high(Component){
  return class extends React.Component{
    constructor(){
      this.state={
        open:false
      }
    }
    componentDidMount(){
      console.log('haha')
    }
    change=()=>{
      this.setState((state)=>{//用到state需要使用回调函数修改state的值
          return {open:!state.open}
      })
    }
    render(){
      return <Component  open={this.state.open}  change={this.change}>
             </Component>
    }
  }
}

class ToggleButton extends Component{//不带有自身的状态能够实现组件的复用
  constructor(props){
    super(props)
  }
  render(){
    let {open,change}=this.props;
    return <Fragment>
              <button type="primary" onClick={change}> 
                toggle Modal
              </button>
              <div>{open}</div> //拿到open值
          </Fragment>
  }
}
const EnhancedComponent = high(ToggleButton);

内置hooks

  • useState

像是 function 形式的 setState,其实这等价于依赖注入,与使用 setState 相比,这个组件是没有状态的。

当不使用hooks时,需要使用class创建组件,使用state作为内部私有状态

class ToggleBotton extends React.Component{
  constructor(props){
    super(props);
    this.state={open:false}
  }
  toggle=()=>{
    this.setState((state)=>{
      return {open:!state.open}
    })
  }
  render(){
    <Fragment>
      <button type="primary" onClick={this.toggle}> 
        toggle Modal
      </button>
      <div>{this.state.open}</div> //拿到open值
    </Fragment>
  }
}

使用hooks的useState

function ToggleBotton() {
  const [open, setOpen] = useState(false);
  //useState接收一个默认值(可以是任何数据类型),
  //返回数组有一对值,不带set的用来取值,带set的用来修改值,可以取任何名字
  const toggle=()=>{
    setOpen(!open)//直接使用setOpen修改open值,不用使用setState
  }
  return (
    <Fragment>
      <button type="primary" onClick={toggle}> 
        toggle Modal
      </button>
      <div>{open}</div> //直接用 open取值,不用使用this.state.open
    </Fragment>
  );
}

useState在一个组件内可以多次调用,也可以将参数设置为对象数据类型,减少调用

const [instete, setInstete] = useState({aa:1,bb:2,cc:3...});
  • useEffect

  1. 可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
  2. 接收一个回调函数做参数
  3. 本要在componentWillUnmount中要编写的逻辑,放在 useEffect执行的回调函数中 返回的一个新函数中。。。
  4. 一个组件中可以调用多个useEffect,不同的逻辑放在各自的useEffect中,使逻辑更加清晰,当组件初始或重新渲染时会依次执行所有的useEffect
  5. 可以通过跳过 Effect 进行性能优化
useEffect(() => {
  console.log('useEffect 的代码既会在初始化时候执行,也会在后续每次重新渲染时执行')
  const subscription = props.source.subscribe();
  return () => {//返回的函数用来清除计时器,解绑事件啥的,防止内存泄漏
    //相当于componentWillUnmount要编写的逻辑,如果没有这些需求也可以不写
    subscription.unsubscribe();
  };
});

以上代码等价于

componentDidMount(){
  console.log('useEffect 的代码既会在初始化时候执行,也会在后续每次重新渲染时执行')
  const subscription = props.source.subscribe();
}
componentDidUpdate(){
  console.log('useEffect 的代码既会在初始化时候执行,也会在后续每次重新渲染时执行')
  const subscription = props.source.subscribe();
}
componentWillUnmount{
  subscription.unsubscribe();
}

effect的性能优化(useEffect的第二个参数)

//第二个参数是一个数组,说明当数组里边的数据发生变化的时候才会触发调用useEffect的回调
useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时才更新,避免多余的调用

如果只想在初始化的时候实现依次调用的化,第二个参数写成空数组 [ ] 即可

自定义hooks(解决状态共享)

1. hooks的规则
  • 不要在循环,条件或嵌套函数中调用 Hook
  • 不要在普通的 JavaScript 函数中调用 Hook
    ✅ 在 React 的函数组件中调用 Hook
    ✅ 在自定义 Hook 中调用其他 Hook
  • Hook 函数必须以 "use" 命名开头
2.自定义hooks的使用
  • 自定义 Hook 是一个函数,其名称必须以 “use” 开头,函数内部可以调用其他的 Hook。
  • 函数式组件都可以使用这个自定义的hook
  • 在两个组件中使用相同的 Hook 不会共享 state,不同组件之间的state没有关系,所以也不能实现数据的持久化
  • 你可以创建涵盖各种场景的自定义 Hook,如表单处理、动画、订阅声明、计时器。。。
import React, { useState, useEffect } from 'react';

//定义自定义hook
function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });
  return isOnline;
}


//使用自定义hook
function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);
  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

hooks的特点

  • 多个状态不会产生嵌套,写法还是平铺的(renderProps 可以通过 compose 解决,可不但使用略为繁琐,而且因为强制封装一个新对象而增加了实体数量)。
  • Hooks 可以引用其他 Hooks。
  • 更容易将组件的 UI 与状态分离。

最后的目标是----有状态的组件没有渲染,有渲染的组件没有状态

状态和ui分离,便于逻辑的复用

相关文章

网友评论

      本文标题:react的新特性(二)---hooks

      本文链接:https://www.haomeiwen.com/subject/sxqsfctx.html