1、react 中 a 标签链接报错:Warning: A future version of React will block javascript: URLs as a security precaution.
当项目中
a
标签的 href 值为javascript:;
或者javascript:void()
时,在编译中会报错,具体报错信息如下:
Warning: A future version of React will block javascript: URLs as a security precaution.
Use event handlers instead if you can. If you need to generate unsafe HTML try using dangerouslySetInnerHTML instead. React was passed “javascript:;”.
解决方案:href
后面不用赋值,但要给点击事件加上 preventDefault
,即:
<a onClick={(e) => e.preventDefault()}>点我</a>
<!-- 如果需要判断href,像下面这样写可以 -->
<a href="link ? link : '#'" onClick={(e) => !link && e.preventDefault()}>点我</a>
2、‘XXX’ implicitly has an ‘any’ type
TS 项目在接收参数时使用了参数解构,遇到了如下警告提示: Binding element ‘XXX’ implicitly has an ‘any’ type
遇到这种解构参数识别不到类型的,只需要给该参数补上类型声明即可。
rowType
为 row
对象的类型声明,当然也可以直接声明为 any
。
const text= ({ row }: { row: rowType })=>{
// ...
}
3、typescript 报错 Cannot find name “__dirname”
这通常是由于 tsconfig.js 配置的问题
1). 将 node
添加到 tsconfig.json
文件中的 compilerOptions.types
{
"compilerOptions": {
...
"types": [
"node"
]
...
}
}
2). 安装@types/node
yarn add @types/node --save-dev
4、动态加载/切换组件
使用 React.lazy
和 React.Suspense
来加载即可,这样也方便后面配合 webpack
的 splitchunks
做代码拆分。
import React, { Suspense, lazy, useState, useCallback } from "react";
import "./styles.css";
export default function App() {
const [DynamicModule, setDynamicModule] = useState("div");
const onClick = useCallback((e) => {
let newName = e.target.dataset.name;
setDynamicModule(
lazy(() => import(/* webpackMode: "eager" */ `../modules/${newName}`))
);
}, []);
return (
<div className="App">
<button data-name="moduleA" onClick={onClick}>
按钮 A
</button>
<button data-name="moduleB" onClick={onClick}>
按钮 B
</button>
<div>
内容区
<div>
<Suspense fallback={<div>"loading"</div>}>
<DynamicModule />
</Suspense>
</div>
</div>
</div>
);
5、hooks 中使用 await
useEffect
的回调参数返回的是一个清除副作用的函数。因此无法返回 Promise
,更无法使用 async/await
,那我们有什么办法来让它支持 async/await
呢?
1). 方法一(推荐):useEffect
中异步函数采用 IIFE
写法( Immediately Invoked Function Expression 即立即调用的函数式表达式)
const MyFunctionnalComponent: React.FC = props => {
useEffect(() => {
// Using an IIFE
(async function anyNameFunction() {
await loadContent();
})();
}, []);
return <div></div>;
};
2). 方法二:此时可以选择再包装一层 async
函数,置于 useEffect
的回调函数中,变相使用 async/await
const fetchMyAPI =async()=> {
let response = await fetch('api/data')
response = await res.json()
dataSet(response)
}
useEffect(() => {
fetchMyAPI();
}, []);
3). 方法三:在内部创建一个异步函数,等待 getData(1)
结果,然后调用 setData()
:
useEffect(() => {
const fetchMyAPI = async () =>{
const data = await getData(1);
setData(data)
}
fetchMyAPI();
}, []);
6、TypeScript 项目报错 Cannot use import statement outside a module, Unknown file extension “.ts“
解决方法:只需要在 tsconfig.js
里添加 "module": "commonjs"
即可。
7、useEffect 中清除 setTimeout 等副作用
useEffect 函数体中是当前的副作用,它也可以返回一个函数用来清除当前的副作用。
useEffect(() => {
let timer = setTimeout(() => {}, 1000)
return ()=> clearTimeout(timer)
}, [])
8、从枚举类型中提取 key 或 value 组成的联合类型(枚举类型转联合类型)
有时候,我们想通过之前定义的枚举类型中提取出可用的联合类型,比如提取其中的
key
或者value
组成的联合类型,以下是相关用法示例:
enum EnumFruit = {
orange: 'Orange',
apple: 'Apple' ,
banana: 'Banana'
}
// 获取枚举类型的 key 组成的联合类型
type KeyUnion = typeof EnumFruit // 'orange' | 'apple' | 'banana'
// 获取枚举类型的 value 组成的联合类型
type ValueUnion = `${EnumFruit}` // 'Orange' | 'Apple' | 'Banana'
9、interface 中的联合类型验证不通过
当我们在
interface
中定义了一个类型为联合类型的属性,在赋值的时候就会出现这类错误,如下示例:
interface Fruit {
fruit: 'Orange' | 'Apple' | 'Banana',
count: number
}
const fruitList:Fruit[] = [{fruit: 'Apple', count: 6 }]
// Type 'string' is not assignable to type '"Orange" | "Apple" | "Banana"'
到这种情况可以考虑使用 对象字面量
以及 typeof
和 keyof
推断相关类型:
const FruitObj = {
orange: 'Orange',
apple: 'Apple' ,
banana: 'Banana'
} as const // 一定要加 as const,不然没用
interface Fruit {
fruit: typeof FruitObj[keyof typeof FruitObj]
count: number
}
const fruitList:Fruit[] = [{fruit: 'Apple', count: 6 }]
参考文章:
10、Type ‘undefined’ cannot be used as an index type
当我们在 TS 中使用中括号动态访问对象中的某个属性或者数组中的某个值时,可能会提示这个错误。主要是由于 TS 无法确定这个动态属性的值导致的。如下例子所示:
// 👇️ key is optional (could be undefined)
const obj1: { key?: string } = {
key: 'name',
};
const obj2 = {
name: 'James Doe',
};
// ⛔️ Error: Type 'undefined' cannot be
// used as an index type.ts(2538)
const result = obj2[obj1.key];
因此,我们需要对这个动态属性添加类型守卫(type guard
),在使用前判断一下,如果不为 undefined
才使用,并且使用 as
把它断言为对象的 key
,代码如下:
if (obj1.key != undefined) {
result = obj2[obj1.key as keyof typeof obj2];
}
console.log(result); // 👉️ "James Doe"
参考:Type ‘undefined’ cannot be used as an index type in TS
11、‘React’ refers to a UMD global, but the current file is a module. Consider adding an import instead
出现这个错误应该是 ts 的版本和 React 有冲突,可以通过配置 tsconfig.json 文件中的 jsx 属性解决。相关 issue
"jsx": "preserve",
12、路由懒加载的场景下,项目发版后导致没有刷新的用户出现js文件加载失败: ChunkLoadError: Loading chunk 42 failed.
这种情况主要是由于浏览器缓存导致的,一般为了避开缓存的影响,我们打包出来的文件名都会带上唯一的
hash
值,但是当我们发版本后,那些没有刷新页面的用户在切换页面的时候,就会出现加载 js 出错的问题,而且一般发生在单页面的场景下。
解决方法:
- 去除
index.html
文件的缓存(Nginx 中的缓存设置排除一下 html 文件) - 为路由文件配置 ErrorBoundary,提示用户刷新,本质是利用
componentDidCatch
生命周期
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null, errorInfo: null };
}
componentDidCatch(error, errorInfo) {
// Catch errors in any components below and re-render with error message
this.setState({
error: error,
errorInfo: errorInfo
})
// You can also log error messages to an error reporting service here
}
render() {
if (this.state.errorInfo) {
// Error path
return (
<div>
<h2>Something went wrong.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
// Normally, just render children
return this.props.children;
}
}
class BuggyCounter extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(({counter}) => ({
counter: counter + 1
}));
}
render() {
if (this.state.counter === 5) {
// Simulate a JS error
throw new Error('I crashed!');
}
return <h1 onClick={this.handleClick}>{this.state.counter}</h1>;
}
}
function App() {
return (
<div>
<p>
<b>
This is an example of error boundaries in React 16.
<br /><br />
Click on the numbers to increase the counters.
<br />
The counter is programmed to throw when it reaches 5. This simulates a JavaScript error in a component.
</b>
</p>
<hr />
<ErrorBoundary>
<p>These two counters are inside the same error boundary. If one crashes, the error boundary will replace both of them.</p>
<BuggyCounter />
<BuggyCounter />
</ErrorBoundary>
<hr />
<p>These two counters are each inside of their own error boundary. So if one crashes, the other is not affected.</p>
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
13、The operand of a ‘delete’ operator must be optional
当我们在 TS 中使用 delete 删除对象的属性时,可能会遇到这个错误。
interface Person {
name: string
age: number
}
const person:Person = { name:'Jack', age: 20 }
delete.name // The operand of a 'delete' operator must be optional
在 strictNullChecks
中使用 delete
运算符时,操作数现在必须为 any
、unknown
、never
或为可选(因为它在类型中包含 undefined
)。我们只需要确保要删除的那个属性为可选类型即可,如下:
interface Person {
name?: string
age: number
}
const person:Person = { name:'Jack', age: 20 }
delete.name
14、svg 压缩后,viewbox 属性丢失,导致图标展示异常
在使用
@svgr/webpack
插件压缩svg
之后,发现线上出现图标尺寸展示异常,经过检查,发现是 svg 的viewbox
属性被移除导致的,此时只需要修改插件的配置即可。相关 issue
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
exclude: /node_modules/,
use: [
{
loader: '@svgr/webpack',
options: {
svgoConfig: {
plugins: [
{ name: 'removeViewBox', active: false } // 保留 viewbox 属性,避免图标展示异常
]
}
}
}
]
}
15、react 中渲染 html
react
中需要通过dangerouslySetInnerHTML
这个属性来渲染html
内容
dangerouslySetInnerHTML
是React
标签的一个属性,类似于angular
的ng-bind
。- 有 2 个
{{}}
,第一个{}
代表jsx
语法开始,第二个是代表dangerouslySetInnerHTML
接收的是一个对象键值对。 - 既可以插入
DOM
,又可以插入字符串。
<div dangerouslySetInnerHTML={{__html: processResponsiveText(content)}}></div>
16、The expected type comes from property ‘onClick’ which is declared here on type xxx
出现这个错误主要是由于 TS 无法推断出当前
event
的类型导致的,我们只需要为event
对象指定相应的类型即可,常用的可能会有React.MouseEvent<HTMLDivElement>
、React.MouseEvent<HTMLButtonElement>
,如下例子使用到了React.MouseEvent<HTMLDivElement>
这个类型:
参考:Typescript and React: What is the proper type for an event handler that uses destructuring?
const handleClick = ({
currentTarget
}: React.MouseEvent<HTMLDivElement>) => {
setAnchorEl(currentTarget)
}
<div onClick={handleClick}>点击我</div>
17、Warning: validateDOMNesting(…): ‘td’ cannot appear as a child of ‘div’
在使用 MUI 的
TablePagination
组件的时候,我遇到了这个问题,提示说的是td
不应该作为div
的子元素,这个组件内部使用了datagrid
这个插件,他默认以 div 渲染,但是这个插件默认以table
来渲染,最终 TS 会提示这个错误。
经过查阅文档,发现该组件提供了一个 component
属性,可以指定它以何种元素来渲染。如下代码:
<div className="w-full flex justify-end">
<TablePagination
sx={{ border: 'none' }}
rowsPerPageOptions={[]}
component="div"
colSpan={3}
count={total}
labelDisplayedRows={() => null}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
ActionsComponent={TablePaginationActions}
/>
</div>
18、React version not specified in eslint-plugin-react settings
在
eslintrc.js
或者.eslintrc
文件中添加如下配置项,让它自行检查版本信息即可:
{
"settings": {
"react": {
"version": "detect"
}
}
}
19、Received true
for a non-boolean attribute xxx
当我们在 React 中开发自定义组件的时候,如果定义了一个布尔类型的
prop
,在使用这个组件的时候,如果我们直接传递布尔值或者只传递单个属性,React 都会抛出这个异常。
// 定义一个Child组件
const Child = ({visible}:{visible:boolean}) => {
return <div>{visible && <span>666</span>}</div>
}
export default Child
// 使用Child组件
<Child visible/> // Received `true` for a non-boolean attribute
<Child visible={true}/> // Received `true` for a non-boolean attribute
这是 React 本身的限制(因为不带引号的 HTML 属性可能会导致许多问题),你需要改变一下写法:
<Child visible={1}/> // 用 0 和 1 代替布尔值
<Child visible="true"/> // 直接传递字符串形式(注意组件内部判断)
// 还有如下写法也会抛出同样的异常
// ❌ This will throw the above error
<Component className={hidden && 'hidden'} />
// ✔️ Use a ternary instead
<Component className={hidden ? 'hidden' : undefined} />
参考:How to fix the ‘Received “true” for a non-boolean attribute’ error
🤠 希望能给遇到同样相关问题的你一点帮助,也为了记录。
评论区