美文网首页
react冷门小知识 - 未完待续

react冷门小知识 - 未完待续

作者: 娜姐聊前端 | 来源:发表于2021-02-28 13:35 被阅读0次

    1. react合成事件 SyntheticEvent(React 17和老版本对比)

    React 17 以前

    React在原生的DOM事件上封装了一层,称为SyntheticEvent(合成事件)。所有事件都会冒泡到根节点 - document上,然后进行后续处理。

    事件绑定根节点
    • 优势1:兼容各类浏览器的Dom事件
    • 优势2:事件池。

    事件池中装满了SyntheticEvent对象,需要时池中取出使用,用完后再放回池中,这就意味着 SyntheticEvent对象可以被缓存且反复使用。目的是提高性能,减少创建不必要的对象。

    当调用事件回调函数之后,合成对象上的所有属性重置为null,但是合成事件对象依旧存在。

    因此,写React事件回调函数的时候不能将 event 用于异步操作 —— 当异步操作真正执行的时候,SyntheticEvent对象有可能已经被重置了。

    import React, { Component } from "react";
    
    class TextInput extends Component {
      state = {
        counter: 0,
        value: this.props.defaultValue,
      }
      // 由于 setState 是异步操作,event.target.value 在运行时可能已经被重置了
      handleChange = event => 
        this.setState(prevState => ({ value: event.target.value, counter: prevState.counter + 1 }));
    
      render() {
        return (
          <span>Edited {this.state.editionCounter} times</span>
          <input
            type="text"
            value={this.state.value}
            onChange={this.handleChange} // WRONG!
          />
        )
      }
    }
    

    一般有两种解法:

    1. 使用 event.persist() 持久化合成事件:将当前event挪出事件池,从而该event属性值可以一直存在而不会被重置。
      • 缺点:放弃了SyntheticEvent事件复用能力,不推荐
    2. 缓存所需的event属性值
      • 缺点:代码略啰嗦。但是,推荐
    import React, { Component } from "react";
    
    class TextInput extends Component {
      state = {
        counter: 0,
        value: this.props.defaultValue,
      }
      
      handleChange = event => {
        const value = event.target.value; // value这个本地变量已经保存了目标值
        this.setState(prevState => ({ value, editionCounter: prevState.counter + 1 }));
      }
    
      render() {
        return (
          <span>Edited {this.state.editionCounter} times</span>
          <input
            type="text"
            value={this.state.value}
            onChange={this.handleChange}
          />
        )
      }
    }
    

    如要想要阻止冒泡,不要直接调用原生e.stopPropagation()方法,没用的。
    利用合成事件上的nativeEvent访问到原生事件,再调用nativeEvent.stopImmediatePropagation方法。
    https://developer.mozilla.org/zh-CN/docs/Web/API/Event/stopImmediatePropagation

    React 17

    变化1:事件绑定将从document下移到root节点,

    事件绑定root点

    变化2:Web 端的 React 17 不使用事件池(https://zh-hans.reactjs.org/docs/legacy-event-pooling.html)。由于 SyntheticEvent 不再放入事件池,e.persist() 将不再生效。
    为什tReact 17 废弃了事件复用机制呢?因为在现代浏览器下这种性能优化没有意义,反而给开发者带来了困扰

    2. 加了key就一定好吗?(此知识点React和Vue通用)

    渲染列表时,大家都知道要加key值,为什么呢?为了配合diff算法做性能优化呀?

    那么如果是纯文字改动呢?

    <ul>
    <li id="1">1</li>
    <li id="2">2</li>
    <li id="3">3</li>
    <li id="4">4</li>
    </ul>
    
    // 更新为
    <ul>
    <li id="1">1</li>
    <li id="2">3</li>
    <li id="3">4</li>
    <li id="4">5</li>
    </ul>
    

    如果指定了key,需要做移动,删除,新增,三种操作。但是,我们仅仅更新个textContent,需要这么大动作吗?
    那就不要指定key好了,React(或者Vue)会复用原DOM节点,只做textContent更新而已,性能明显更好。

    当然,如果节点上绑定了事件,且事件和节点内容相关,那么请务必小心,为了不必要的Bug,还是建议加上key

    3. 不要直接将prop赋值给state

    场景:子组件用props初始化state数据。

    3.1 先看看class模式的案例代码:

    class Child extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          list: props.list
        }
      }
    
      render() {
        return (
          <div>
            {this.state.list.map((item, index) => {
              return <h1 key={index}>Hello, {item.name}</h1>
            })}
          </div>
        )
      }
    }
    
    class Parent extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          list: [{ name: 'lily' }, { name: 'bob' }]
        }
      }
    
      handleCilck = () => {
        this.setState({
          list: this.state.list.concat([{ name: 'parent' }])
        })
      }
    
      render() {
        return (
          <div>
            <button onClick={this.handleCilck}>父组件更新state</button>
            <Child list={this.state.list} />;
          </div>
        )
      }
    }
    

    点击父组件更新state按钮,子组件的列表会更新吗?
    当然是No。因为子组件的constructor只会在初始化时运行,所以更新阶段是不会对state重新复制。
    解决方案也比较简单,利用componentWillReceiveProps钩子函数触发子组件state更新。

    componentWillReceiveProps(props) {
        this.setState({
          list: props.list
        })
    }
    

    3.2 useState开发模式

    function Component (props) => {
        const [name, setName] = useState(props.name)
        console.log(name) 
    }
    

    同样的,如果props更新,子组件依旧用第一次的值来渲染。
    可以利用uesEffect解决这个问题:

    function Component (props) => {
        const [name, setName] = useState(props.name)
        useEffect(() => {
           setName(props.name)
        }, [props.name])
    }
    

    相关文章

      网友评论

          本文标题:react冷门小知识 - 未完待续

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