ArthurWangCN / notepad

reading notepad
0 stars 2 forks source link

React interview #52

Open ArthurWangCN opened 1 week ago

ArthurWangCN commented 1 week ago

React事件机制

什么是合成事件

React基于浏览器的事件机制实现了一套自身的事件机制,它符合W3C规范,包括事件触发、事件冒泡、事件捕获、事件合成和事件派发等

React事件的设计动机(作用):

React事件机制和原生DOM事件流有什么区别

虽然合成事件不是原生DOM事件,但它包含了原生DOM事件的引用,可以通过e.nativeEvent访问

ArthurWangCN commented 6 days ago

组件之间传参方法

父子间通信

这种父子通信方式也就是典型的单向数据流, 父组件通过 props 传递数据, 子组件不能直接修改 props, 而是必须通过调用父组件函数的方式告知父组件修改数据

  1. 父组件通过 props 传递数据给子组件
  2. 子组件通过调用父组件传来的 函数 传递数据给父组件(自定义事件)
  3. 非常规方法: 父组件通过 ref 获取子组件的实例对象

兄弟间通信

状态提升: 在父组件中创建共同的状态、事件函数, 其中一个兄弟组件调用父组件传递过来的事件函数修改父组件中的状态, 然后父组件将状态传递给另一个兄弟组件

任意组件之间进行通信

使用 Context:

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

function App({ children }) {
  const theme = useContext(ThemeContext);
  return (<div>{theme}</div>)
}

function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <App />
    </ThemeContext.Provider>
  )
}

或使用 Redux 等状态管理工具

ArthurWangCN commented 6 days ago

受控组件和非受控组件

受控组件

组件内部 state 或值完全受 prop 控制的组件

就像 antd 里 Input 组件, 可以通过 props 传一个 value 使得 Input 变为受控组件, Input 组件内部状态(值)就由 props 控制

import { Input } from 'antd';
<Input value="写死或者设置为状态值"/>

非受控组件

组件内部 state或值不受 props 控制的组件, 由组件内部自己管理

就像 antd 里 Input 组件, 如果不给组件传 value 值, 那么组件就是非受控组件, Input 组件内由自己管理 value, 这时如果要想拿到表单的 value 则只能通过 ref 等手段, 手动获取

注意的是: Input 组件内部, 使用了 input 标签将 value 和状态进行绑定, 那么对于 input 标签来说它是受控的, 所以受控组件只是相对

import { Input } from 'antd';
<Input/>

什么时候使用受控组件、什么时候使用非受控

当组件内部值或状态和外部存在交互逻辑时, 则需要将其作为受控组件进行使用

  1. 当组件状态(值)只由自身交换控制, 不受外部影响时, 可使用非受控组件: 比如 Antd Input 组件, 如果输入框的内容只随着用户输入时改变, 那么就可以使用非受控组件
  2. 当组件状态(值)除了受自身交换控制、还受到外部影响时, 可使用受控组件: 比如 Antd Input 组件, 需要和其他控件产生联动对组件的值进行相应的格式化
  3. 当组件状态(值)和外部需要交换时, 可使用受控组件: 比如 Antd 单选框, 当选中时需要隐藏页面上内容时, 一般就会将单选框最为受控组件进行使用
ArthurWangCN commented 2 days ago

Ref 相关

作用

  1. 在函数组件中, 当我们希望组件能够 记住 或者说 存储 某些信息, 但呢又不希望该信息触发新的渲染时, 就可以使用 ref 来存储
  2. 用于访问真实 DOM 元素
  3. 当父组件需要获取子组件实例对象时, 也可通过 ref 来实现

获取真实 DOM: 三种方式

  1. 推荐使用 API: React.createRef()、useRef
    
    // 类组件, 使用 createRef
    this.ref = React.createRef();
    <div ref={this.ref}></div>

// 函数组件, 使用 useRef const ref = React.useRef();


2. ref 回调函数方式

```js
// 类组件
bindRef = ele => {
  this.bodyRef = ele;
};
<div ref={this.bindRef}></div>

// 函数组件
const bindRef = useCallback((ele) => {}, []);
<div ref={bindRef}></div>
  1. 字符串(仅限类组件中使用)
// 会自动在 this 上绑定 bodyRef, 等于当前元素
<div ref="bodyRef"></div>

获取子组件实例

  1. 子组件为类组件, 直接绑定 ref, 就能够拿到整个子组件的实例对象
class A extends Component {}

const App = () => {
  const ref = useRef()
  return (<A ref={ref}/>)
}
  1. 函数组件: forwardRef + useImperativeHandle
// 子组件
const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef();
  // 通过 useImperativeHandle 将子组件的方法暴露给父组件
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    getValue: () => {
      return inputRef.current.value;
    }
  }));
  return <input type="text" ref={inputRef} />;
});

// 父组件
const childRef = useRef();
return (
  <div>
    <ChildComponent ref={childRef} />
  </div>
)

解释:

子组件 ChildComponent:

  1. 使用 forwardRef 包装子组件,使其能够接收 ref。
  2. 使用 useImperativeHandle 将子组件的方法(如 focus 和 getValue)暴露给父组件。

父组件 ParentComponent:

  1. 使用 useRef 创建一个 ref,并将其传递给子组件。
  2. 通过这个 ref 调用子组件中暴露的方法(如 focus 和 getValue)
ArthurWangCN commented 2 days ago

Fragment

在 React 中如果需要渲染多个元素, 需要使用元素进行包裹, 否则将会报错。报错原因, 主要原因还是在 JSX 编译这块:

// 编译前
const dom = (
  <ChildA />
  <ChildB />
  <ChildC />
);

// 编译后, 这样很明显是有问题的
const dom = (
  React.createElement(……) 
  React.createElement(……) 
  React.createElement(……)
);

上面错误代码解决办法就是, 使用 div 等标签进行包裹, 这样就能够通过编译,但是会添加了额外节点, Fragment 出现就为了解决上面的问题, 通过 Fragment 可以将子列表分组, 最终在渲染为真实 DOM 节点时会将其忽略(不会进行渲染)。