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>
);
}
}
看看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;
这个时候this.props.btnRef
为undefined,开始怀疑给cloneElement传递ref压根就不行,做个改变,让children渲染
输出:
结论:
给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,下面两种情况都不会报警
函数定义在外部
function geek(params: any) {} const Click = useCallback(geek, []);
函数定义在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)
截图:
解决办法,使用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无效,如图:
在组件中切换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可能就是文档的宽高,做了尝试
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,这样高亮的定位就很准确了,定位的坐标是后端解析通过接口给到的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。