使用 React 渲染 jQuery 组件:如何编写 useEffects?

问题描述 投票:0回答:1

我们有一个包含许多遗留 jQuery 组件的代码堆栈。我们正在转向 React,其中一个步骤就是用 React 包装 jQuery。

但是,像这样管理非 React 子组件的状态似乎并不是 useEffects 常见的内容。

es-lint/exhaustive-deps
不喜欢我的任何解决方案。我已经查看了 https://overreacted.io/a-complete-guide-to-useeffect/ 和 React 文档,但我仍然不确定正确的答案是什么。

朴素的函数组件如下所示:

const MyReactFunctionComponent = (props) => {
  const element = useRef(null);
  const [JQueryComp, setJQueryComp] = useState(null);

  const renderJQueryHelper = () => {
    // Not 1-1 props match, lot of transformation and helper functions
    const JQueryProps = { ...props };
    return new myJQueryComponent(JQueryProps, element.current);
  };

  useEffect(() => {
    // only heavy render on first mount
    setJQueryComp(renderJQueryHelper());
    return () => {
      JQueryComp.destroy();
    };
  }, []); // warn: missing deps 'JQueryComp' and 'renderJQueryHelper'
  
  // call update on every reRender, comp diffs the props itself.
  if (JQueryComp) {
    JQueryComp.update(props);
  }

  return <div ref={element} />;
};

理论上,我可以将整个助手移动到 useEffect 中,但这很快就会变得一团糟,我想避免这种情况。遵循各种指南,我找到了这个解决方案,用

useRef
来存储
useCallback

  const renderJQueryHelper = useCallback(() => { ..., [props]);

  const helperRef = useRef(renderJQueryHelper);

  useEffect(() => {
    setJQueryComp(helperRef.current());
    ...

这适用于辅助函数,我已经在其他地方使用过它。但它不包括 JQueryComp,我需要能够调用 destroy。它也不能处理我想要更频繁地运行繁重渲染助手的情况,例如 jQuery 组件崩溃,或者其他更复杂的情况。我觉得我一定错过了什么。 我将包含 JQueryComp 的示例实现,以及它在类组件中的外观,它看起来要简单得多。

const myJQueryComponent = (props, element) => { const $element = $(element); $element.addClass('my-JQuery-component'); const initialize = () => { // lots of JQuery code here, attaching containers, event listeners, etc. // eventually renders other JQuery components }; const render = () => { if ($element.children().length > 0) { $element.trigger('JQuery_COMP_UPDATE', props); } else { initialize(); } }; this.update = _.debounce((newProps) => { if (newProps.type !== props.type) { this.destroy(); } if (!_.isEqual(newProps, props)) { props = newProps; render(); } }, 100); this.destroy = () => { $element.trigger('JQuery_COMP_DESTROY').empty(); }; render(); }; class MyReactClassComponent extends React.Component { renderJQueryHelper() { // Not 1-1 props match, lot of transformation and helper functions const JQueryProps = {...props} return new myJQueryComponent(JQueryProps, this.element); } componentDidMount() { this.JQueryComp = renderJQueryHelper(); } componentDidUpdate() { if (!this.JQueryComp) { // JQuery comp crashed? this.JQueryComp = renderJQueryHelper } this.JQueryComp.update(this.props); } componentWillUnmount() { if (this.JQueryComp) { this.JQueryComp.destroy(); } } render() { return <div ref={(element) => (this.element = element)} />; } }

	
javascript jquery reactjs react-hooks
1个回答
0
投票
myJQueryComponent

组件/对象引用可以存储在另一个 React 引用中。


创建第二个 React 引用来保存对
    myJQueryComponent
  1. 对象的引用。
    使用
  2. mounting空依赖数组,因此效果只运行一次useEffect钩子回调(
    类似于React类组件的
    componentDidMount生命周期方法
    )来实例化
    myJQueryComponent
    并返回清理函数(
    类似于类组件的 
    componentWillUnmount 生命周期方法
    )在组件卸载时销毁当前 
    myJQueryComponent
    对象。
    使用第二个 
  3. useEffect
  4. 钩子来处理组件生命周期,其中
    props
    值随时间变化,并用作触发更新
    myJQueryComponent
    对象的依赖项(
    类似于类组件的 
    componentDidUpdate 生命周期方法
    ) .
  5. const MyReactFunctionComponent = (props) => { const elementRef = useRef(null); const jQueryCompRef = useRef(); useEffect(() => { const jQueryProps = { ...props }; jQueryCompRef.current = new myJQueryComponent( JQueryProps, elementRef.current ); return () => { jQueryCompRef.current.destroy(); }; // NOTE: mounting effect only // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { jQueryCompRef.current.update(props); }, [props]); return <div ref={element} />; };
如果上面的方法不太有效,并且 React 仍然需要一点点才能知道它应该重新渲染,那么您可以在必要时强制组件重新渲染。

示例:

const useForceRerender = () => { const [, setState] = useState(false); // useCallback is used to memoize a stable callback return useCallback(() => setState(c => !c), []); };

const MyReactFunctionComponent = (props) => {
  const elementRef = useRef(null);
  const jQueryCompRef = useRef();

  const forceRerender = useForceRerender();

  useEffect(() => {
    const jQueryProps = { ...props };

    jQueryCompRef.current = new myJQueryComponent(
      JQueryProps,
      elementRef.current
    );

    return () => {
      jQueryCompRef.current.destroy();
    };
    // NOTE: mounting effect only
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    jQueryCompRef.current.update(props);
    forceRerender();
  }, [forceRerender, props]);
  
  return <div ref={element} />;
};
	
© www.soinside.com 2019 - 2024. All rights reserved.