我正在尝试编写一个自定义的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
的内容。然而,React钩子没有this
集,而且我似乎找不到关于获得对功能组件上下文的引用的文档。
---
编辑:我不想传递任何参数,也不想添加一堆锅炉板。我的目标是useLogging()
函数将在组件重构中幸存下来,而不依赖于开发人员提供";正确的";名称
我认为还有其他几种方法可以使用。Drew在评论中建议的第一个也是最简单的一个,就是简单地将日志记录名称作为参数传递:
const useLogger = (name: string) => {
return logManager.getLogger(name)
}
const Login: React.FC<Props> = () => {
const log = useLogger('Login')
// ...
}
您也可以通过.displayName
或.name
获取名称。请注意,.name
指的是Function.name
,如果您使用的是webpack,它可能会在生产构建中被缩小,因此您最终会得到类似";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
的东西,它旨在为你提供一个nameof
运算符,就像C#中一样:
const useLogger = (name: string) => {
return logManager.getLogger(name)
}
const Login: React.FC<Props> = () => {
const log = useLogger(nameof(Login))
// ...
}
这里的好处是,该名称将在自动重命名后继续存在。这需要一些bundler或Babel配置。我还没有测试缩小是如何影响这一点的,但有三种不同风格的ts-nameof
(在撰写本文时(可以使用:
- TypeScript编译器的编译时转换:ts的名称
- Babel宏:ts-nameeof.macro
- Babel插件:Babel插件的名称
选择第一个与构建管道匹配的管道。
或者,如果记录器不是组件特定的,而是模块特定的,您可以为挂钩创建一个工厂,并在模块顶部初始化一次:
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,例如globals.d.ts
,在其中为Typescript:声明__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()
// ...
}
但是,该属性已被弃用,所以您迟早要清理它,所以在生产代码中不要这样做。