1

1. 同步拿到setState后的数据

  • 使用setTimeout
import React from "react";

class SetStateExample extends React.Component {
  state = { count: "" };
  handleChange = (e) => {
    // const val = e.target.value;
    setTimeout(() => {
      // e.persist();
      console.info(e.target);
      // this.setState({ count: val });
      console.info(this.state.count);
    }, 0);
  };
  render() {
    return (
      <div>
        <div>
          <label htmlFor="">请输入:</label>
          <input
            type="text"
            className="text"
            onChange={this.handleChange}
            value={this.state.value}
          />
        </div>
        <p>{this.state.count}</p>
      </div>
    );
  }
}

export default SetStateExample;

结果报错
Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property target on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist(). See [https://fb.me/react-event-pooling](https://fb.me/react-event-pooling) for more information.

跟着提示,加上e.persist();

  handleChange = (e) => {
    e.persist();
    // const val = e.target.value;
    setTimeout(() => {
      // e.persist();加在这里依然报错
      this.setState({ count: e.target.value });
      console.info(this.state.count);
    }, 0);
  };

或者使用setState的一个Synthetic Event Warning提到的解决方式,将事件的值赋值给一个变量

  handleChange = (e) => {
    const val = e.target.value;// 在setTimeout外面去赋值给一个变量
    setTimeout(() => {
      this.setState({ count: val });
      console.info(this.state.count);
    }, 0);
  };
  • 使用回调函数
  handleChange = (e) => {
    const val = e.target.value;
    this.setState({ count: val }, () => {
      console.info(this.state);
    });
  };
  • 使用 await setState
import React from "react";

export default class App extends React.Component {
  state = {
    count: 0
  };
  handleClick = async () => {
    console.info("pre:", this.state.count);
    await this.setState(({ count }) => ({ count: ++count }));
    console.info("next:", this.state.count);
  };
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>增加</button>
        <p>{this.state.count}</p>
      </div>
    );
  }
}

image.png

看看setState是什么类型的

 setState<K extends keyof S>(
  state:
    | ((prevState: Readonly<S>, props: Readonly<P>) => Pick<S, K> | S | null)
    | (Pick<S, K> | S | null),
  callback?: () => void
): void;

setState返回值是void,并不是Promise,为啥还能起作用?

es7 async await 为什么会对react setState起作用?
我所理解的 Hooks API

2. 调用函数组件

在项目里发现有人直接调用函数组件,该组件是用了hooks的,超出了我的认知。

React Functional component: calling as function vs. as component
Don't call a React function component

3. 通过cloneElement也可以将ref绑定到组件上

通常ref传递是直接绑定在组件上,特殊情况也可以用cloneElement进行绑定,比如react-resizable@3.0.4 Resizable.js

export default class Resizable extends React.Component<Props, void> {
  // Render a resize handle given an axis & DOM ref. Ref *must* be attached for
  // the underlying draggable library to work properly.
  renderResizeHandle(handleAxis: ResizeHandleAxis, ref: ReactRef<HTMLElement>): ReactNode {
    const {handle} = this.props;
    // No handle provided, make the default
    if (!handle) {
      return <span className={`react-resizable-handle react-resizable-handle-${handleAxis}`} ref={ref} />;
    }
    // Handle is a function, such as:
    // `handle={(handleAxis) => <span className={...} />}`
    if (typeof handle === 'function') {
      return handle(handleAxis, ref);
    }
    // Handle is a React component (composite or DOM).
    const isDOMElement = typeof handle.type === 'string';
    // 传递ref
    const props = {
      ref,
      // Add `handleAxis` prop iff this is not a DOM element,
      // otherwise we'll get an unknown property warning
      ...(isDOMElement ? {} : {handleAxis})
    };
    return React.cloneElement(handle, props);

  }
  render(): ReactNode {
    // Pass along only props not meant for the `<Resizable>`.`
    // eslint-disable-next-line no-unused-vars
    const {children, className, draggableOpts, width, height, handle, handleSize,
            lockAspectRatio, axis, minConstraints, maxConstraints, onResize,
            onResizeStop, onResizeStart, resizeHandles, transformScale, ...p} = this.props;

    // What we're doing here is getting the child of this element, and cloning it with this element's props.
    // We are then defining its children as:
    // 1. Its original children (resizable's child's children), and
    // 2. One or more draggable handles.
    return cloneElement(children, {
      ...p,
      className: `${className ? `${className} ` : ''}react-resizable`,
      children: [
        ...children.props.children,
        ...resizeHandles.map((handleAxis) => {
          // Create a ref to the handle so that `<DraggableCore>` doesn't have to use ReactDOM.findDOMNode().
          const ref = (this.handleRefs[handleAxis]) ?? (this.handleRefs[handleAxis] = React.createRef());
          return (
            <DraggableCore
              {...draggableOpts}
              nodeRef={ref}
              key={`resizableHandle-${handleAxis}`}
              onStop={this.resizeHandler('onResizeStop', handleAxis)}
              onStart={this.resizeHandler('onResizeStart', handleAxis)}
              onDrag={this.resizeHandler('onResize', handleAxis)}
            >
              {this.renderResizeHandle(handleAxis, ref)}
            </DraggableCore>
          );
        })
      ]
    });
  }
}

刚看到上面代码很不理解,在DraggableCore中进行debug,nodeRef有时候有值,有时候没值。百思不解,写个demo试试。

import React, { useEffect, useRef } from 'react';

class Inner extends React.Component<any> {
  componentDidMount() {
    console.info('inner btnRef:', this.props.btnRef);
    console.info('inner children:', this.props.children);
  }
  render() {
    return <div>inner</div>;
  }
}

class Button extends React.Component {
  render() {
    return <button>提交</button>;
  }
}

const Wrapper = () => {
  const ref = useRef();
  const props = {
    ref,
  };

  const NextButton = React.cloneElement(<Button></Button>, props);

  useEffect(() => {
    console.info(ref);
    console.info(NextButton);
  }, []);

  return (
    <div>
      <Inner btnRef={ref}>{NextButton}</Inner>
    </div>
  );
};

export default Wrapper;

ref.current 为undefined

这个时候this.props.btnRef为undefined,开始怀疑给cloneElement传递ref压根就不行,做个改变,让children渲染

children渲染结束

输出:

image.png

结论:

给cloneElement传递ref是可以拿到被克隆组件的引用的,但是该组件必须被渲染,否则ref拿不到组件的引用。

4. useCallback跟lodash debounce结合报警

  const Change = useCallback(
    debounce((e: React.ChangeEvent<HTMLInputElement>) => {
      console.info(e.target.value);
    }, 500),
    [],
  );

上述写法会报警:React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead.eslintreact-hooks/exhaustive-deps

eslint-plugin-react-hooks 报警

react-hooks/exhaustive-deps throws lint error Pass an inline function for valid scenarios #19240 给出了解决办法,使用useMemo

useCallback is specifically designed for inline functions. For cases like this you need to use useMemo:

 const throttledMethod = React.useMemo(
        () => _.throttle(abc, 500 ),
        [abc],
    );

什么是 inline function?

const callback = () => () => {};

const Click = useCallback(callback(), []);

这样就报警,被函数返回的函数应该就不是inline function,下面两种情况都不会报警

  1. 函数定义在外部

    function geek(params: any) {}
    
    const Click = useCallback(geek, []);
  2. 函数定义在useCallback内部

    const Click = useCallback(() => {}, []);

    从不报警推断上述两种都是inline function

5. ant table columns 传入被styled-components包裹的StyledTable报错

报错信息:

Type 'User' does not satisfy the constraint 'string | ComponentType<any>'.
  Type 'User' is not assignable to type 'FunctionComponent<any>'.
    Type 'User' provides no match for the signature '(props: any, context?: any): ReactElement<any, any> | null'.ts(2344)

截图:
image.png

解决办法,使用styled-components泛型

import { Table } from "antd";
import { ColumnsType } from "antd/es/table";
import styled from "styled-components";
import { TableProps } from "antd/lib/table";

const StyledTable = styled<React.FC<TableProps<User>>>(Table)`
  .ant-table-cell {
    font-size: 14px;
  }
`;

interface User {
  key: number;
  name: string;
}

const columns: ColumnsType<User> = [
  {
    key: "name",
    title: "Name",
    dataIndex: "name"
  }
];

const data: User[] = [
  {
    key: 0,
    name: "Jack"
  }
];

export default () => (
  <>
    <StyledTable columns={columns} dataSource={data} />
  </>
);

6. 强制引用文件名的大小写

团队协作,有的小伙伴经常import的文件名跟实际的文件名大小写不一致,本地Windows构建是没问题,导致在docker编译中报错,只好强制在项目里做检测。

const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');

_.plugin('CaseSensitivePathsPlugin').use(CaseSensitivePathsPlugin);

7. react组件中改变svg颜色

在react项目中配置svgr,就可以把svg文件作为组件使用,

import { ReactComponent as Logo } from './logo.svg';

checkbox选中未选中两个状态怎么去切换svg图标的颜色,参考How do I add color to my svg image in react在 React 中改变 SVG 颜色,在本地做了实验,发现只需要在svg设置stroke="currentColor",设置为current无效,如图:
image.png

在组件中切换svg颜色:

<item.icon stroke={item.name === currentPath ? "black" : "white"}></item.icon>;

框选的两个fill不需要设置。

8. 自定义hooks里设置state,页面无法重新渲染

When custom React Hooks do not rerender Components on their own – make them.

9. umi-request 无法获取文件数据

如何下载application/octet-stream文件?

10. react-pdf-highlighter@6.1.0 高亮定位不准

官方demo里width,height两个参数没有明确的解释

      position: {
        boundingRect: {
          x1: 255.73419189453125,
          y1: 139.140625,
          x2: 574.372314453125,
          y2: 165.140625,
          width: 809.9999999999999,
          height: 1200,
        },
        rects: [
          {
            x1: 255.73419189453125,
            y1: 139.140625,
            x2: 574.372314453125,
            y2: 165.140625,
            width: 809.9999999999999,
            height: 1200,
          },
        ],
        pageNumber: 1,
      },

issue提供了思路,翻看当时的代码的解释coordinates.js

// "scaled" means that data structure stores (0, 1) coordinates.
// for clarity reasons I decided not to store actual (0, 1) coordinates, but
// provide width and height, so user can compute ratio himself if needed

考虑到可能上述参数的width,height可能就是文档的宽高,做了尝试
image.png

          pdfDocument.getPage(1).then((page) => {
            const viewport = page.getViewport({ scale: 1 });
            const width = viewport.width;
            const height = viewport.height;
            setWidthAndHeight(width, height);
          });

export const useGetChunkHighlights = (selectedChunk: IChunk) => {
  const [size, setSize] = useState({ width: 849, height: 1200 });

  const highlights: IHighlight[] = useMemo(() => {
    return buildChunkHighlights(selectedChunk, size);
  }, [selectedChunk, size]);

  const setWidthAndHeight = (width: number, height: number) => {
    setSize((pre) => {
      if (pre.height !== height || pre.width !== width) {
        return { height, width };
      }
      return pre;
    });
  };

  return { highlights, setWidthAndHeight };
};

获取到pdf的宽高再传递给highlights得到正确的position,这样高亮的定位就很准确了,定位的坐标是后端解析通过接口给到的。


assassin_cike
1.3k 声望74 粉丝

生活不是得过且过


« 上一篇
中间件
下一篇 »
学学css吧