# 条件渲染下子组件内部的 hook 重置

  1. 当父组件使用条件渲染(例如通过一个状态或属性决定是否渲染某个子组件)时,如果条件从 true 变为 false ,然后又变回 true ,这个过程确实会导致 React 重新创建该子组件实例。这意味着子组件内部的 useState (以及其他钩子的状态)将会被重置,因为每次条件为真且子组件被重新挂载时,都会初始化一个新的状态。

  2. 例子

function ParentComponent() {
  const [showChild, setShowChild] = useState(true);
  return (
    <div>
      <button onClick={() => setShowChild(!showChild)}>
        Toggle Child
      </button>
      {showChild && <ChildComponent />}
    </div>
  );
}
function ChildComponent() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase</button>
    </div>
  );
}

在这个例子中,每当点击 Toggle Child 按钮时, showChild 的值会切换,从而控制 ChildComponent 的显示和隐藏。如果 ChildComponentshowChildfalse 而卸载,那么它的状态(如这里的 count )也会丢失。当再次设置 showChildtrue 时, ChildComponent 会被重新挂载,并且其状态会重新初始化。
3. 在条件变化时避免子组件及其状态的丢失

  • 使用持久化存储:例如 localStoragesessionStorage 来保存子组件的状态并在组件重新挂载时恢复。
  • 将状态提升到父组件:将需要保持的状态移到父组件中管理,并通过属性传递给子组件。这种方法可以让状态在子组件卸载和重新挂载之间得以保持。
  • 使用 <React.Fragment> 或其他方式避免完全移除组件:比如通过样式控制组件的可见性而不是通过条件渲染来添加或移除组件。

# 父组件调用子组件的函数

  1. 子组件使用 forwardRefuseImperativeHandle
    首先,在子组件中使用 forwardRef 来接收从父组件传递过来的 ref ,并通过 useImperativeHandle 自定义暴露给父组件的方法。
import React, { forwardRef, useImperativeHandle, useState } from 'react';
const Togglable = forwardRef((props, ref) => {
    const [visible, setVisible] = useState(false);
    const toggleVisibility = () => {
        setVisible(!visible);
    };
    // 使用 useImperativeHandle 暴露特定方法给父组件
    useImperativeHandle(ref, () => ({
        toggleVisibility
    }));
    const hideWhenVisible = { display: visible ? 'none' : '' };
    const showWhenVisible = { display: visible ? '' : 'none' };
    return (
        <div>
            <div style={hideWhenVisible}>
                <button onClick={toggleVisibility}>{props.buttonLabel}</button>
            </div>
            <div style={showWhenVisible}>
                {props.children}
                <button onClick={toggleVisibility}>取消</button>
            </div>
        </div>
    );
});
export default Togglable;
  1. 父组件创建 ref 并传递给子组件
    在父组件中,创建一个 ref 并通过属性将其传递给子组件。然后,你可以直接通过这个 ref 调用子组件暴露的方法。
import React, { useRef } from 'react';
import Togglable from './Togglable'; // 假设 Togglable 组件在同一目录下
function ParentComponent() {
    const childRef = useRef();
    return (
        <>
            {/* 将 ref 传递给子组件 */}
            <Togglable ref={childRef} buttonLabel="显示/隐藏">
                这是一个可切换显示的内容。
            </Togglable>
            
            {/* 调用子组件中的方法 */}
            <button onClick={() => childRef.current.toggleVisibility()}>
                从父组件调用切换方法
            </button>
        </>
    );
}
export default ParentComponent;

forwardRef :用于让子组件能够接收到父组件传递的 ref
useImperativeHandle :允许你自定义暴露给父组件的实例属性或方法。在这个例子中,我们只暴露了 toggleVisibility 方法。
ref :在父组件中创建并传递给子组件,通过这个 ref 可以直接访问子组件暴露的方法。