react浅析

吐槽君 分类:javascript

1. react 16 版本生命周期有哪些?

  • 初始化阶段
    • constructor 构造函数
    • getDefaultProps props 默认值
    • getDefaultState state 默认值
  • 挂载阶段
    • componentDidMount 组件已经被渲染到 DOM 中 (对应 mounted)
    • render
  • 更新阶段
    • shouldComponentUpdate
    • componentDidUpdate
  • 卸载阶段
    • componentWillUnmount (对应 destoryed)
  • 错误处理
    • componentDidCatch

2. props 和 state 的区别?

  • props 是外部传入的数据参数, 不可变;
  • state 是组件内部的状态, 可变;
  • 没有 state 的是无状态组件, 有 state 的是有状态组件;
  • 多用 props, 少用 state, 即多写无状态组件

3. setState 是同步的还是异步的?

  • 结论: 原生事件, setTimeOut 是同步的, 合成事件, 生命周期中是异步的

  • 异步示例

    componentDidMound(){
      this.setState({index: this.state.index + 1});
      console.log(index)
    }
 
  • 同步示例
    componentDidMound(){
      setTimeout(()=>{
        console.log(this.state.index)
        this.setState({index: this.state.index + 1});
        console.log(this.state.index)
      })
    }
 

4. react hook

hook 是 react 16.8 的新增特性, 他可以让你在不编写 class 的情况下使用 state 和其他的 react 属性

  • 基础示例:
  import React, { useState } from "react";
    function Example() {
      const [count, setCount] = useState(0);
      return (
        <div>
          <p>点击{{ count }}次</p>
          <button onClick={() => setCount(count + 1)}></button>
        </div>
      );
    }
 
  • Hook 声明多个 state 变量
  function Example() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("xiaoming");
  }
 
  • Effect Hook
    • 副作用: 如果在 react 组件中, 执行数据获取, 订阅或手动修改 DOM, 统一叫做副作用
    • useEffect 钩子, 给函数组件增加了操作副作用的能力, 它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API
    // 示例:
    import React, { useState } from "react";
    function Example() {
      const [count, setCount] = useState(0);
      useEffect(() => {
        document.title = `点击了${count}次`; // 完成对DOM的更改后执行useEffect中的代码
      });
      return (
        <div>
          <p>点击{count}次</p>
          <button onClick={() => setCount(count + 1)}></button>
        </div>
      );
    }
 
清除副作用: 副作用函数可以通过返回一个函数来清除副作用.
 
    // 下面的组件使用副作用来订阅好友的在线状态, 并通过取消订阅来清除操作;
    import React, { useState, useEffect } from "react";

    function FriendStatus(props) {
      const [isOnline, setIsOnline] = useState(null);

      function handleStatusChange(status) {
        setIsOnline(status.isOnline);
      }

      useEffect(() => {
        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
        return () => {
          ChatAPI.unsubscribeFromFriendStatus(
            props.friend.id,
            handleStatusChange
          );
        };
      });

      if (isOnline === null) {
        return "Loading...";
      }
      return isOnline ? "Online" : "Offline";
    }
 
  • 自定义 hook
    • 如果在组件之间有一些重用的状态逻辑, 有两种主流方案可以解决, 高阶组件和 render props, 自定义 hook 可以让你在不增加组件的情况下实现
    // 自定义hook示例:
    function useFriendStatus(friendID) {
      const [isOnline, setIsOnline] = useState(null);

      function handleStatusChange(status) {
        setIsOnline(status.isOnline);
      }

      useEffect(() => {
        ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
        return () => {
          ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
        };
      });
      return isOnline;
    }
    // 以friendID为参数, 并返回该好友是否在线
    // 在以下两个组件中使用
    function findFriendStatus(props) {
      const isOnline = useFriendStatus(props.friend.ID);
      if (isOnline === null) {
        return "Loading...";
      }
      return isOnline ? "Online" : "Offline";
    }

    function FriendListItem(props) {
      const isOnline = useFriendStatus(props.friend.id);

      return (
        <li style={{ color: isOnline ? "green" : "black" }}>
          {props.friend.name}
        </li>
      );
    }
 

可以在自定义 hook 中传递数据

    function transData() {
      const [id, setId] = useState(0);
      const isOnline = findFriendStatus(id);
    }
 
  • hook 规则

      1. 不要在循环, 条件或嵌套函数中调用 hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们.
        遵循这条规则, 可以保证每一次渲染都按照同样的顺序被调用, 这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确
      // 示例:
          function Form() {
       // 1. Use the name state variable
       const [name, setName] = useState("Mary");
      
       // 2. Use an effect for persisting the form
       useEffect(function persistForm() {
         localStorage.setItem("formData", name);
       });
      
       // 3. Use the surname state variable
       const [surname, setSurname] = useState("Poppins");
      
       // 4. Use an effect for updating the title
       useEffect(function updateTitle() {
         document.title = name + " " + surname;
       });
      
       // ...
      }
       
      1. 只能在 react 函数中调用 hook

5. 高阶组件

定义: 高阶组件的参数为组件, 返回值是新组件的函数

  // 示例:
    const commentList = withSubscription(CommentList, DataSource =>
      DataSource.getComment()
    );

    const BlogPost = withSubscription(BlogPost, (DataSource, props) => {
      DataSource.getBlogPost(props.id);
    });

    function withSubscription(WrappedComponents, selectedData) {
      return class extends React.components {
        constructor(props) {
          super(props);
          this.handleChange = this.handleChange.bind(this);
          this.state = {
            data: selectedData(DataSource, props)
          };
        }
        componentDidMount() {
          // ...负责订阅相关的操作...
          DataSource.addChangeListener(this.handleChange);
        }

        componentWillUnmount() {
          DataSource.removeChangeListener(this.handleChange);
        }

        handleChange() {
          this.setState({
            data: selectData(DataSource, this.props)
          });
        }
        render() {
          return <WrappedComponents data={this.state.data} {...this.props} />;
        }
      };
    }
 

6. render props

定义: react 组件之间, 使用一个值为函数的 prop 共享代码的简单技术

    //  示例1:
    <DataProvider render={data => <h1>{data.target}</h1>} />;

    // 示例2
    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 Mouse extends React.Component {
      constructor(props) {
        super(props);
        this.handleMouseMove = this.handleMouseMove.bind(this);
        this.state = { x: 0, y: 0 };
      }

      handleMouseMove(event) {
        this.setState({
          x: event.clientX,
          y: event.clientY
        });
      }

      render() {
        return (
          <div style={{ height: "100vh" }} onMouseMove={this.handleMouseMove}>
            {/*
          使用 `render`prop 动态决定要渲染的内容,
          而不是给出一个 <Mouse> 渲染结果的静态表示
        */}
            {this.props.render(this.state)}
          </div>
        );
      }
    }

    class MouseTracker extends React.Component {
      render() {
        return (
          <div>
            <h1>移动鼠标!</h1>
            <Mouse render={mouse => <Cat mouse={mouse} />} />
          </div>
        );
      }
    }

    // 示例3:
    // 如果你出于某种原因真的想要 HOC,那么你可以轻松实现
    // 使用具有 render prop 的普通组件创建一个!
    function withMouse(Component) {
      return class extends React.Component {
        render() {
          return (
            <Mouse
              render={mouse => <Component {...this.props} mouse={mouse} />}
            />
          );
        }
      };
    }
 

7. react-redux

  • 定义: 状态管理框架

  • 流程图:
    avatar

  • 基本概念:

    • store: react-redux 总的状态容器, 是一个对象
    • action: 一个对象, 表明事件, 需要有 type 字段
    • reducer: 一个函数, 根据不同的 action 返回不同的数据
      根据上面流程图, view 层可以通过两种方式来更新

      • view 发出 action => dispatcher 之后到达 reducer => reducer 处理后返回新的数据更新 View
      • 其他层发出 action 后以同样的方式更新 view
  • 总结: 发出 action => reducer 根据 action 返回不同数据 => store 被更新 => view 更新

组件分类: react-redux 将所有组件分为 UI 组件 和 容器组件

  • UI 组件:

    • 只负责 UI 的展示
    • 没有状态(state)
    • 所有数据由 props 提供
    • 不使用 redux 的所有 API
  • 容器组件

    • 负责管理数据和所有的交互逻辑
    • 带有内部状态
    • 可以使用 redux 的 API
  • 计数器示例:

    import { connect } from "react-redux";
    import Count from "./Count";
    function mapStateToProps(state) {
      return {
        count: state.count
      };
    }
    function mapDispatchToProps(dispatch) {
      return {
        add: () => dispatch({ type: "ADD_COUNT" })
      };
    }

    const newComponent = connect(mapStateToProps, mapDispatchToProps)(Count);
 

mapStateToProps

接收 state 参数, 返回 props 对象

mapDispatchToProps

接收 dispatch 参数, 定义一系列发送事件的方法(发送一个 action), 返回 props

Reducer

刚才我们发送了 action, 但是并未对事件进行处理, reducer 实际就是来处理 action 的
示例如下:

    // counter-reducer
    export default function reducer(state = { count: 0 }, action) {
      switch (action.type) {
        case "ADD-COUNT":
          return {
            count: state.count + 1
          };
        default:
          return state;
      }
    }
 

store

   // store示例
    import { createStore, Provider } from "react-redux";
    import React from "react";
    import reducer from "./counter-reducer";
    import Counter from "./components/Counter";

    const store = createStore(reducer); // 创建store
    ReactDOM.render(
      // store注册到顶层, 所有组件可以共享
      <Provider store={store}>
        <Counter />
      </Provider>,
      document.getElementById("root") // 挂载到根节点
    );
 
  • redux 总结:
    • dispatch(action) => reducer => new state => new props => update components
    • 分为容器组件和 UI 组件, 传统组件需要使用 connect 处理
    • reducer 处理 action 返回新的 state, 需要考虑 action 不匹配的情况(default)
    • 使用 creatStore 函数创建 store,reducer 作为参数
    • 使用 provider 作为顶层组件将全局 sotre 引入

8. react fiber

react 在进行组件渲染时, 从 setState 到渲染完成整个过程是同步的, 如果需要渲染的组件比价庞大, js 运行占用的主线程时间会比较长, 导致页面响应变差, 使得 react 在动画, 手势等应用中效果变差;
为了解决整个问题, react 团队经过两年时间, 重写了 react 的核心算法-reconciliation, 命名为 fiber

本文持续更新中~~~

回复

我来回复
  • 暂无回复内容