八股(自己整理)React篇1

91 阅读6分钟

1.组件通信方式:

父组件向子组件:props

const Child=props=>{
    return <p>{props.name}</p>
}
const Parent=()=>{
   return <Child name="hello"></Child>
}

子组件向父组件:props+回调

const Child= props =>{
      const fn= msg =>{
        return () =>{props.callback(msg)}
      }
      return( <button onClick={fn('你好!')}>你好</button> )
}
class Parent extends Component{
       callback(msg){
             console.log(msg)
       }
       render(){
            return <Child callback={this.callback.bind(this)}></Child>
       }
}

兄弟组件:找到共同的父节点,结合上面两种方式

跨层级通信:context

创建一个context对象

const MyContext=React.createContext('light')
class App extends React.Component{
     return (
         <MyContext.Provider value="dark">
             <Toolbar/>
         </MyContext.Provider>
     );
}
//中间的组件不必指明往下传递

发布订阅模式:

非嵌套关系的组件:全局状态管理工具rudex

2.路由

<BrowserRouter>

<HashRouter>

通过监听hashchange事件感知hash变化

3.Hooks

为函数组件补齐类组件的功能

常用Hooks:

按功能分类:

数据更新驱动:useState useReducer

执行副作用:useEffect useLayoutEffect

状态获取与传递:useContext useRef

状态派生与保存:useMemo useCallback

①useState

useState返回的是数组,解构的时候可以对数组中的元素命名

const [state,setState]=useState(initialState)

set函数是异步更新

②useEffect

执行一段与当前组件渲染无关的副作用代码

接受两个参数:回调函数和依赖数组

useEffect(() => {
  // 副作用逻辑
}, [dependencies]); // 依赖数组

a.不传依赖数组,每次渲染时都执行

b.依赖数组为空数组时,只在挂载时执行

c.有依赖项,首次挂载和依赖数组改变时执行

清理机制:

useEffect可以返回一个清理函数

执行时机:

1组件卸载时:组件从DOM中移除时,执行最后一次副作用的清理函数

2副作用重新执行前:依赖数组发生变化,先执行副作用的清理函数,再执行新的副作用

典型应用场景:

useEffect(() => {
  const interval = setInterval(() => {
    console.log('定时器运行中');
  }, 1000);
  
  // 清理函数:组件卸载或依赖项变化时清除定时器
  return () => {
    clearInterval(interval);
    console.log('定时器已清除');
  };
}, []); // 空依赖数组表示只在挂载时创建定时器

react渲染流程整体分为两个阶段:render阶段和commit阶段

render阶段就是reconcile 的 vdom 转 fiber 的过程

render function执行后产生React Element对象,也就是vdom

vdom会转换为fiber解构

commit 阶段就是具体操作 dom,以及执行副作用函数的过程

分为三个小阶段:before mutation,mutation,layout

mutation阶段操作dom

③useLayoutEffect

同步执行

在DOM更新之后,浏览器绘制之前

④useMemo

用于缓存计算值

接受两个参数:一个计算函数和一个依赖数组

避免在每次渲染时重新计算复杂的值

减少不必要的计算

⑤useCallback

用于缓存函数

接受两个参数:一个函数和一个依赖数组

依赖数组的值发生变化时,重新创建函数

⑥useContext

用于跨层级通信

⑦useRef

接收一个状态作为初始值,返回一个ref对象

const cur = React.useRef(initState)
console.log(cur.current)

4.状态逻辑复用

状态逻辑复用:

将组件中可复用的状态管理逻辑(如数据请求、表单处理、定时器等)抽离出来,

供多个组件共享,减少代码冗余提高维护性

常见方案:HOC 自定义Hook Render Props

HOC

高阶组件是一个函数,接收一个组件作为参数,返回一个新的增强组件

通过包装组件的方式,注入复用的状态逻辑和props

// 定义高阶组件:处理计数器逻辑
function withCounter(WrappedComponent) {
  return class extends React.Component {
    state = { count: 0 };

    increment = () => this.setState({ count: this.state.count + 1 });

    render() {
      // 将状态和方法通过 props 传递给被包装组件
      return (
        <WrappedComponent
          count={this.state.count}
          increment={this.increment}
          {...this.props} // 透传外部 props
        />
      );
    }
  };
}

// 使用高阶组件
const CounterButton = withCounter(({ count, increment, label }) => (
  <button onClick={increment}>
    {label}: {count}
  </button>
));

// 复用:不同组件共享计数器逻辑
<CounterButton label="点赞数" />
<CounterButton label="收藏数" />

缺点:可能导致包装地狱(多层HOC嵌套)

静态方法需手动赋值,props可能被覆盖

自定义Hook

自定义Hook是一个以use开头的函数,内部可以调用其他Hook

组件通过调用自定义hook直接获取状态和方法

// 定义自定义 Hook:处理计数器逻辑
function useCounter(initialCount = 0) {
  const [count, setCount] = React.useState(initialCount);
  const increment = () => setCount(prev => prev + 1);
  const decrement = () => setCount(prev => prev - 1);
  return { count, increment, decrement }; // 返回状态和方法
}

// 组件中使用
function CounterDisplay() {
  const { count, increment } = useCounter(); // 直接调用 Hook 获取逻辑
  return <button onClick={increment}>计数:{count}</button>;
}

function DoubleCounter() {
  const { count, increment } = useCounter(10); // 可传入初始值,灵活复用
  return <button onClick={increment}>翻倍计数:{count * 2}</button>;
}

优势:简洁直观,可组合多个hook

缺点:必须遵循hook原则,只能在函数组件或自定义hook中使用,不能在条件语句中使用

Render Props

通过一个返回React元素的prop,将复用的状态逻辑传递给组件

组件通过调用该prop渲染内容,接收逻辑中的状态和方法

// 定义 Render Props 组件:处理鼠标位置逻辑
class MouseTracker extends React.Component {
  state = { x: 0, y: 0 };

  handleMouseMove = (e) => {
    this.setState({ x: e.clientX, y: e.clientY });
  };

  componentDidMount() {
    window.addEventListener('mousemove', this.handleMouseMove);
  }

  componentWillUnmount() {
    window.removeEventListener('mousemove', this.handleMouseMove);
  }

  render() {
    // 调用 render prop,将状态传递给外部
    return this.props.render(this.state);
  }
}

// 使用 Render Props
function MousePosition() {
  return (
    <MouseTracker
      render={({ x, y }) => ( // 通过 render prop 接收状态
        <div>鼠标位置:({x}, {y})</div>
      )}    />  );
}

// 复用:不同组件共享鼠标位置逻辑
function CatOnMouse() {
  return (
    <MouseTracker
      render={({ x, y }) => (
        <img
          src="cat.png"
          style={{ position: 'absolute', left: x, top: y }}
          alt="跟随鼠标的猫"
        />      )}    />  );
}

5.React.Router的实现

React 生态中用于实现单页应用(SPA)路由管理的核心库

在不刷新页面的情况下,根据 URL 变化渲染对应的组件,提供导航、路由参数、嵌套路由等功能

路由模式:基于浏览器API实现URL监听和修改

单页应用的路由本质:改变URL但不刷新页面

hash模式

利用URL中的哈希(#及后面的部分)作为路由标识

改变哈希不会触发页面刷新,且浏览器会记录哈希变化的历史

实现:

通过 window.location.hash 读取当前哈希值

监听 hashchange 事件,哈希变化时触发路由更新

导航时(如点击 Link 组件),通过修改 window.location.hash 改变 URL,避免页面刷新

history模式(browserrouter)

基于 HTML5 的 History API(window.history),通过 pushState、replaceState 等方法修改 URL 路径(不含 #),且不触发页面刷新

实现:

通过 window.history.pushState() 或 replaceState() 改变 URL(只修改历史记录,不发送请求)

监听 popstate 事件(用户点击前进 / 后退按钮触发),感知 URL 变化并更新路由

导航时,通过 history.push() 或 history.replace() 封装 pushState/replaceState,实现无刷新跳转

核心组件:

路由更新机制:

状态变化触发组件重渲染

当URL变化时:

URL改变,感知变化,更新location

重新匹配路由,渲染匹配组件