Skip to content

开课吧 React 预习笔记

统计信息:字数 10695 阅读22分钟

  • 01 react-create-app 功能、react/react-dom 的功能,webpack 入口文件配置等等
  • 02 jsx 语法;CSS模块化的实现
  • 03 函数化组件和类组件;函数化组件可以通过hook实现生命周期函数和状态
  • 04 setState 的参数,同步和异步的情况
  • 05 生命周期函数
  • 06 组件复合
  • 07 redux
  • 08 react-redux
  • 09 react-router
  • 10 pureComponent 纯组件,实现state的浅比较;
  • 11 hook useState useEffect
  • 12 自定义hook和注意事项(两个)useName 自定义Hook
  • 13 useMemo useCallback API 只有满足特定条件,才执行内部的函数,返回结果(不会在state变化时,执行全部的操作)

第5课 生命周期函数

React 17 废弃的生命周期函数

componentWillMount

componentWillUpdate

componentWillReceiveProps

新的生命周期函数

static getDerivedStateFromProps

static getSnapshotBeforeUpdate

新函数和旧函数不能同时使用。旧函数可以先加上 UNSAVE 前缀:

npx react-codemod rename-unsafe-lifecycles <path>

getDerivedStateFromProps: 这个用于取代 componentWillReceiveProps 可以从上一个 props 中计算出新的state并重新 setState。

getSnapshotBeforeUpdate(prevProps, prevState) 在 render 之后,在 componentDidUpdate 之前。可以在组件发生更改之前从 DOM 中捕获信息(滚动位置等)。

返回值就传递给 componentDidUpdate(prevProps, prevState, snapshot)函数中

第6课 组件复合

组件复合(组件嵌套使用,高阶组件)父组件给子组件 children 中,可以是直接的JSX或者字符串,也可以传一个对象(属性包括字符串,JSX,函数)子组件中使用 children.obj 获取对应的属性即可

第7课 redux

首先 let store = createStore(reducer); 创建一个 store。

其中 reducer 是更新 store 的函数。reducer(state, action) 根据 action 对象的更新 state。在具体的组件中引入 store。

通过 store.getState 获取状态。通过 store.dispatch({type: 'ADD'}) 触发 action,改变 store 中的状态(state)。

可以在组件中订阅 state 的变化的回调函数(didmount 阶段)。

store.dispatch(() => {回调函数中,this.forceUpdate() 组件强制更新。}) 这样一个周期实现。不同组件可以订阅不同的状态和触发不同的回调函数。

// redux,状态管理工具
// 适应于大量变化的数据+状态需要共享(用于处理由于状态提升,造成顶级组件中 state 非常多难以维护的情况)
// 流程:reducer(current state) -> redux(store) -> view -> dispatch -> action(change state) -> reducer
// npm install redux --save
// 如果是某个组件内部的状态,那么不需要使用 redux 进行状态管理

// demo 累加器

// store ReduxStore.js
import { createStore } from 'redux';

const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case "ADD":
      return state + 1
    case 'MINUS':
      return state - 1
    default:
      return state
  }
}

const store = createStore(counterReducer);

export default store;

reduxPage

import React from 'react';
import store from '../store/ReduxStore';

class ReduxPage extends React.Component {

  componentDidMount() {
    store.subscribe(() => {
      this.forceUpdate();
    });
  }

  add = () => {
    store.dispatch({ type: "ADD" });
  }

  minus = () => {
    store.dispatch({ type: 'MINUS' });
  }

  render() {
    console.log(store);
    return (
      <p>{store.getState()}</p>
    );
  }
}

export default ReduxPage;

或者在顶级组件中监听事件并更新全部的内容

在 index 中订阅状态变更

import store from './store/reduxStore';

const render = () => {
  ReactDOM.render(
    <App/>,
    document.getElementById('#root')
  );
}
render();
store.subscribe(render);

操作要点

  • createStore 创建 store
  • reducer 初始化 state
  • getState 获取 state
  • dispatch 提交更新
  • subscribe 变更订阅

第8课 react-redux

Provider 包裹APP顶层组件,使得全局可以直接使用Store <Provider store={store}><App/></Provider> connect 在子组件中获取 store。

connect(connectStatetoProps, connectDispatchToProps)(Component); 其中第二个括号中是组件。

第一个括号中传递两个参数(state2Props, dispatch2props,分别表示把state 和 dispatch 映射到 prop 中)

这样,在子组件中,不需要直接使用 store.getState 和 store.dispatch 传值,直接使用 { state, dispatch } = this.props 获取即可。

dispatch 可以更改成一个对象,存储多个对应的函数。redux 适合其他库,react-redux 专门用于react 中使用状态。connect 是一个高阶组件。

// npm install react-redux --save
// provider 提供 store
// connect 提供数据变更操作

import React from 'react';
import ReactDom from 'react-dom';
import App from './App';
import store from './store';
import { Provider } from 'react-redux';

ReactDom.render(
  <Provider store={store}>
    <App/>
  </Provider>,
  document.getElementById('#root')
);

// 获取状态数据
import React from 'react';
import { connect } from 'react-redux';

class ReactReduxPage extends React.Component {
  render() {
    const { num, add, minus } = this.props;
    return (
      <div></div>
    );
  }
}

const mapStateToProps = state => {
  return {
    num: state,
  };
};

const mapDispatchToProps = {
  add: () => {
    return { type: 'add' };
  },
  minus: () => {
    return { type: 'minus' };
  }
};

// 高阶组件
export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(ReactReduxPage);

// 参数1:state映射
// 参数2:事件映射

第9课

Router 组件进行包裹,Link 表示跳转的链接,Route 是根据不同URL进行跳转(可以精确匹配,可以进行传参),参数中 children > component > render 。

可以使用 switch 包裹一组 Router 实现选择其中一个进行渲染,主要用于404错误界面的处理。

// npm install react-router-dom --save

import React from 'react';
import { BrowserRouter as Router, Link, Route } from 'react-router-dom';

export default class Text extends React.Component {
  render() {
    return (
      <Router>
        <Link to="/">首页</Link>
        <Link to="/user">用户</Link>
        <Route
          exact
          path='/'
          // children={() => <span></span>}
          component={HomePage}
          // render={() => <hr/>}
        />
        <Route
          path="/user"
          component={UserPage}
        />
      </Router>
    );
  }
}

子组件是唯一的:children > component > render exact 表示精确匹配 <Switch></Stitch> 表示选择,只能选择一个情况,这样可以处理404的情况(前边几个都不满足,最后一个Route path 不传参,显示空页面的提醒) Link 实质是山A标签;route会根据URL的不同,跳转到不同的子页面(组件)中 当然,这部分python-urls(Django)模板已经实现了传参

第10课 pureComponent

pureComponent 实现了 shouldComponentUpdate

实现了数据的浅对比,适合于UI展示界面

最终减少界面render优化性能

import React from 'react';

export default class Demo extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      counter: 0,
      data: {
        num: 0
      }
    };
  }
  // setState({ counter++ })
  // setState({ data.num++ })
}

// 内部逻辑实现
function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (Object.is(objA, objB)) {
    return true;
  }
  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
   return false;
  }
  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);
  if (keysA.length !== keysB.length) {
    return false;
  }
  for (let i = 0; i < keysA.length; i++) {
    if (
      !hasOwnProperty.call(objb, keysA[i]) ||
      !Object.is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;
    }
  }
  return true;
}

第11课

import React, { useState } from 'react';

export default function HookPage(props) {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}></button>
  );
}

// 更新后的组件(可以大大简化代码)
// 可以尝试把一个类组件优化成一个Hook组件
export default function Page2(props) {
  const [count, setCount] = useState(0);
  const [date, setDate] = useState(new Date());
  useEffect(() => {
    document.title = `test ${count}`;
  }, [count]);
  useEffect(() => {
    const timer = setInterval(() => {
      setDate(new Date());
    }, 1000);
    return () => clearInterval(timer);
  }, []);
}

第12课 自定义 hook

函数组件中使用 Hook 实现状态和生命周期函数

import React,{ useState, useEffect } from 'react';

export function Test(props) {
  const [data, setDate] = useState(new Date());
  useEffect(() => {
    const timer = setInterval(() => {
      setDate(new Date());
    }, 1000);
    // return 这里表示 componentWillUnmount 阶段
    return () => clearInterval(timer);
  }, []);
  // 这里第二个参数表示 componentDidMount 更新触发的条件
  // 如果不加的话,每次组件更新都会触发内部代码执行
  // 现在是空数组,那么在 componentDidMount 阶段都会执行
  return (
    <div>{date.toLocalTimeString()}</div>
  );
}

useEffect 函数相当于:componentDidMount componentDidMount componentWillUnmount 生命周期函数。

hook 注意事项:

  • 只能在函数外层调用,不能再条件、循环、子函数中使用
  • 只能在 react 函数组件、或者自定义 Hook 中使用。不能直接在 原生JS中使用 Hook。

自定义Hook:

function useClock() {
  const [ data, setData ] = useState(new Date());
  useEffect(() => {
    const timer = setInterval(() => {
        let date = new Date();
      setData(date);
    }, 1000);
    return () => clearInterval(timer);
  }, []);
  return data;
}

第13课 useMemo useCallback

useMemo

const date = useMemo(() => {
  return new Date();
  // 只有time改变后,这里才重新执行
  // 其他state变化,这里不会重新执行
}, [time]);

// date 作为参数传递给子组件

useCallback

const fn = useCallback(() => {
  return new Date();
}, [time]);

<Child fn={fn}/>

这两个API参数相同:第一个参数是回调函数,第二个是判断条件。

这两个API可以转换:useCallback(fn, deps) === useMemo(() => fn, deps);


Last update: November 9, 2024