总结25个React高级用法

使用了近两年的React框架,最近将开发过程中使用到的一些"高级"用法整理出来,一方面梳理一下自己的思路;另一方面期待能够帮助有需要的同学。

1. 高阶组件(Higher Order Components, HOCs)

在React中进行逻辑重用的强大模式。

jsx 复制代码
function withLogging(WrappedComponent) {
  return class extends React.Component {
    componentDidMount() {
      console.log(`Logged component: ${WrappedComponent.name}`);
    }
    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

2. 渲染属性(Render Props)

共享代码逻辑时的可选模式。

jsx 复制代码
class MouseTracker extends React.Component {
  state = { x: 0, y: 0 };

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

  render() {
    return (
      <div onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

3. 上下文API(Context API)

跨组件共享数据,减少组件层级传递。

jsx 复制代码
const ThemeContext = React.createContext('light');

class ThemedButton extends React.Component {
  render() {
    return (
      <ThemeContext.Consumer>
        {theme => <Button theme={theme} />}
      </ThemeContext.Consumer>
    );
  }
}

4. 异步组件加载

使用React.lazy提升加载效率。

jsx 复制代码
const LazyComponent = React.lazy(() => import('./SomeComponent'));

function MyComponent() {
  return (
    <React.Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </React.Suspense>
  );
}

5. 使用钩子函数(Hooks)

以函数组件形式使用状态和React特性。

jsx 复制代码
function Example() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

6. 自定义钩子(Custom Hooks)

提取组件逻辑,实现复用。

jsx 复制代码
function useWindowSize() {
  const [size, setSize] = useState([window.innerHeight, window.innerWidth]);
  useEffect(() => {
    const handleResize = () => {
      setSize([window.innerHeight, window.innerWidth]);
    };
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  return size;
}

7. 访问前一个状态值

在hooks中获取前一个状态或属性值。

jsx 复制代码
function Timer() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef();
  useEffect(() => {
    prevCountRef.current = count;
  });
  const prevCount = prevCountRef.current;
  // 使用 prevCount 可参照以前的count值
}

8. 代码拆分

通过动态import()语句进行代码拆分,优化应用加载时间。

jsx 复制代码
import('./Math').then(math => {
  console.log(math.add(16, 26));
});

9. 使用Fragments

无需额外元素包裹组件子列表。

jsx 复制代码
class Table extends React.Component {
  render() {
    return (
      <table>
        <tbody>
          <tr>
            <Columns />
          </tr>
        </tbody>
      </table>
    );
  }
}

class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}

10. 错误边界(Error Boundaries)

捕捉子组件树中JavaScript错误并记录这些信息。

jsx 复制代码
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  static getDerivedStateFromError() {
    return { hasError: true };
  }
  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

11. Portals

将子节点渲染到存在于父组件之外的DOM节点。

jsx 复制代码
class Modal extends React.Component {
  render() {
    return ReactDOM.createPortal(
      this.props.children,
      document.getElementById('modal-root')
    );
  }
}

12. 使用Refs转发

转发refs到组件树更深层次的位置。

jsx 复制代码
const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} {...props}>
    {props.children}
  </button>
));

13. 性能优化------shouldComponentUpdate

减少不必要的渲染,优化性能。

jsx 复制代码
class List extends React.Component {
  shouldComponentUpdate(nextProps) {
    return nextProps.list !== this.props.list;
  }
}

14. 性能优化------使用React.memo

对函数组件应用记忆技术。

jsx 复制代码
const MyComponent = React.memo(function MyComponent(props) {
  /* render using props */
});

15. 使用PropTypes验证属性

确保组件收到正确类型的属性。

jsx 复制代码
MyComponent.propTypes = {
  // 你可以声明一个 prop 是特定的 JS 原始类型。默认
  // 情况下,这些 prop 都是可传可不传的。
  optionalNumber: PropTypes.number,
  
  // 你也可以用 `isRequired` 随着任何其他的校验方法来确保在不传入该 prop 的时候显示一个警告。
  requiredFunc: PropTypes.func.isRequired,
}

16. 使用Context替代Redux

在符合的场景下利用Context实现状态管理,避免过度使用Redux。

jsx 复制代码
const ThemeContext = React.createContext('light');

class ThemeButton extends React.Component {
  // 使用 contextType 读取当前的 theme context。
  // React 会通过 ThemeButton 组件以上最近的匹配的 ThemeProvider 找到当前的 theme 值。
  static contextType = ThemeContext;
}

17. 使用Suspense处理数据获取

对异步数据依赖进行优雅的处理。

jsx 复制代码
const resource = fetchData();

function Component() {
  // 使用 resource.read() 读取异步数据,如果数据还没有被获取完成,Suspense 会显示 fallback 中定义的内容
  const data = resource.read();
  return <div>{data}</div>;
}

18. 使用useReducer管理复杂状态逻辑

在hooks中进行状态管理,替代Redux。

jsx 复制代码
function todosReducer(state, action) {
  switch (action.type) {
    case 'add':
      return [...state, {
        text: action.text,
        completed: false
      }];
    // 其他 action handlers
    default:
      return state;
  }
}

function Todos() {
  const [todos, dispatch] = useReducer(todosReducer, []);

  return (
    <div>
      {todos.map((todo, index) => (
        <Todo key={index} todo={todo} dispatch={dispatch} />
      ))}
    </div>
  );
}

19. 利用动态import实现组件懒加载

结合React.lazy和Suspense,按需加载组件。

jsx 复制代码
const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <React.Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </React.Suspense>
    </div>
  );
}

20. 使用useCallback减少子组件重复渲染

在渲染性能关键的应用中,通过记忆回调函数来减少子组件的不必要渲染。

jsx 复制代码
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

21. 使用useRef访问DOM节点

直接操作DOM节点,实现跨渲染周期的状态共享。

jsx 复制代码
function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

22. 使用useLayoutEffect同步执行副作用

确保在浏览器绘制前,同步更新DOM状态。

jsx 复制代码
function useLockedBody() {
  useLayoutEffect(() => {
    const originalOverflow = window.getComputedStyle(document.body).overflow;
    document.body.style.overflow = 'hidden';
    return () => (document.body.style.overflow = originalOverflow);
  }, []); // Empty array ensures effect is only run on mount and unmount
}

23. 状态提升

当多个组件需要共享状态时,将状态提升至它们的公共父组件。

jsx 复制代码
class Parent extends React.Component {
  state = { color: 'red' };

  handleColorChange = (color) => {
    this.setState({ color });
  };

  render() {
    return (
      <div>
        <Child1 color={this.state.color} onColorChange={this.handleColorChange} />
        <Child2 color={this.state.color} />
      </div>
    );
  }
}

24. 使用useImperativeHandle自定义暴露给父组件的实例值

在使用forwardRef时,自定义父组件可以使用的ref操作。

jsx 复制代码
function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} />;
}
FancyInput = forwardRef(FancyInput);

25. 使用useEffect替代生命周期方法

以函数组件的形式复现生命周期方法的行为。

jsx 复制代码
function Example() {
  useEffect(() => { // 类似 componentDidMount 和 componentDidUpdate:
    // 更新文档的标题
    document.title = `You clicked ${count} times`;

    return () => { // 类似 componentWillUnmount
      // 卸载相关操作
    };
  }, [count]); // 仅在 count 更改时更新
}
相关推荐
耶啵奶膘29 分钟前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^2 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie3 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic3 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿3 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具4 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
清灵xmf4 小时前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
小白学大数据4 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
qq_390161774 小时前
防抖函数--应用场景及示例
前端·javascript
334554325 小时前
element动态表头合并表格
开发语言·javascript·ecmascript