reactjs - 如何从 React 挂钩访问功能组件的名称?
问题描述
我正在尝试编写一个自定义的 React 钩子,useLogging
我想在其中根据正在执行日志记录的组件的名称对日志消息进行上下文化。
例如:
const Login: React.FunctionComponent<IProps> = (props) => {
log = useLogging();
log.info("Hello!")
[...]
应该生产[Login] Hello!
然后,我的自定义钩子需要名称Login
:
export const useLogger = () => {
// "this" is undefined
const loggerName = ??????
return logManager.getLogger(loggerName);
};
在类的上下文中,我正在寻找类似this.constructor.displayName
. 但是,没有this
设置 React 钩子,我似乎找不到有关获取对功能组件上下文的引用的文档。
——
编辑:我宁愿不传递任何参数,也不希望添加一堆样板。我的目标是该useLogging()
函数将在组件重构中幸存下来,而不是依赖开发人员提供“正确”的名称。
解决方案
我可以想到您可以使用的其他几种方法。Drew 在评论中建议的第一个也是最简单的一个,就是简单地将日志记录名称作为参数传递:
const useLogger = (name: string) => {
return logManager.getLogger(name)
}
const Login: React.FC<Props> = () => {
const log = useLogger('Login')
// ...
}
您也可以通过.displayName
或获取名称.name
。请注意,如果您使用.name
webpack Function.name
,它可能会在生产构建中被缩小,因此您最终会得到像“t”或“s”等名称。如果您需要与组件中相同的名称,您可以分配displayName
并让钩子处理它:
const useLogger = (component: React.ComponentType<any>) => {
const name = useLogger(component.displayName || component.name);
return logManager.getLogger(name);
}
const Login: React.FC<Props> = () => {
const log = useLogger(Login)
}
Login.displayName = 'Login';
如果您可以将名称传递给useLogger
,但不想displayName
每次都设置,则可以使用类似的东西ts-nameof
,旨在为您提供C# 中nameof
的运算符:
const useLogger = (name: string) => {
return logManager.getLogger(name)
}
const Login: React.FC<Props> = () => {
const log = useLogger(nameof(Login))
// ...
}
这里的好处是该名称将在自动重命名后继续存在。这需要一些捆绑器或 Babel 配置。我还没有测试过缩小如何影响这一点,但是您可以使用三种不同的ts-nameof
(在撰写本文时):
- TypeScript 编译器的编译时转换:ts-nameof
- 一个 Babel 宏:ts-nameof.macro
- 一个 Babel 插件:babel-plugin-ts-nameof
选择与您的构建管道匹配的第一个。
或者,如果记录器不是特定于组件的,而是特定于模块的,你可以为钩子创建一个工厂,并在你的模块顶部初始化一次:
const makeUseLogger = (name: string) => () => {
return logManager.getLogger(name)
}
// in your module
const useLogger = makeUseLogger('Module name')
const Login: React.FC<Props> = () => {
const log = useLogger()
// ...
}
作为对此的扩展,如果记录器本身实际上不需要成为钩子(不使用其他钩子或需要道具等),只需直接在顶层为您的模块制作一个记录器:
const log = logManager.getLogger('Module name')
const Login: React.FC<Props> = () => {
log.info('hello')
}
此外,如果您不介意项目的目录结构泄漏到生产构建中,您可以使用 webpack 技巧:
// webpack.config.js
module.exports = {
// ...
node: {
__filename: true
}
}
接着
const log = logManager.getLogger(__filename)
在路径为/home/user/project/src/components/Login.ts
且 webpack上下文为的文件中/home/user/project
,__filename
变量将解析为src/components/Login.ts
.
虽然,这可能需要您创建一个 typedef,例如在其中为 Typescriptglobals.d.ts
声明全局:__filename
declare global {
__filename: string;
}
注意:如果您的构建目标是umd
.
作为旁注,从技术上讲,如果您出于某种原因不想将任何参数传递给useLogging
,则可以使用已弃用 Function.caller
的属性,例如
function useLogging() {
const caller = (useLogging.caller as React.ComponentType<any>);
const name = caller.displayName || caller.name;
console.log(name);
return logManager.getLogger(name);
}
const Login: React.FC<Props> = () => {
const log = useLogging()
// ...
}
但是,该属性已被弃用,因此您迟早必须清理它,所以不要在生产代码中这样做。
推荐阅读
- javascript - (ReactJS)“警告:组件正在更改要控制的非受控输入。这可能是由未定义的值更改引起的......”
- bluetooth - HC-05 没有从连接中接收到任何数据
- ruby-on-rails - Rails active_model_serializers 不工作
- c - 如何取消引用函数返回的指针?
- javascript - 如何重置反应原生博览会中的所有字段
- javascript - 如何将缩小的 js 脚本添加到 dom 并执行它?
- python-3.x - 返回列表中每次按位和连续元素的值[python]
- python - 如果找到元音,则取它所在的位置,如果该位置是偶数或奇数,则取和
- html - 调整离子玻片时间,每天更换玻片
- visual-studio-code - 我刚刚在 VS Code 中编写了“hello world”程序(C++),但它显示错误