如何修改JSX大括号对对象/类实例的求值逻辑?
问题分析
你的T类继承了Function,导致实例a是一个函数对象。当React在JSX大括号中处理函数类型的值时,会默认将其视为React组件进行渲染,而非直接转换为字符串——这就是{a}输出<div>...</div>的核心原因,而${a}会触发toString(),a()会触发代理的apply逻辑,因此这两种情况能得到预期结果。
解决方案
以下几种方案均可实现目标,且无需修改JSX语法:
方案1:实现Symbol.toPrimitive钩子
利用JS原生的类型转换符号,强制React在处理对象时返回预期的字符串值:
import React from 'https://esm.sh/react@18.2.0' import jsxToString from "https://cdn.skypack.dev/jsx-to-string@1.4.0"; class T extends Function { foo; constructor(foo) { super(); this.foo = foo; return new Proxy(this, { apply: (target) => target._call() }); } bar() { return this.foo + 'bar'; } toString() { return this.bar(); } _call() { return this.bar(); } // 添加类型转换钩子 [Symbol.toPrimitive](hint) { // 当需要字符串转换时返回bar()结果 if (hint === 'string' || hint === 'default') { return this.bar(); } // 其他情况默认处理 return super[Symbol.toPrimitive]?.(hint); } } const a = new T('foo'); const el1 = (<div>{`${a}`}</div>); const el2 = (<div>{a()}</div>); const el3 = (<div>{a}</div>); console.log(jsxToString(el1)) // "<div>foobar</div>" console.log(jsxToString(el2)) // "<div>foobar</div>" console.log(jsxToString(el3)) // "<div>foobar</div>"
方案2:改为普通类+Proxy实现可调用
放弃继承Function,用普通类配合Proxy让实例可调用。此时a不再是函数对象,React会自动调用其toString()方法:
import React from 'https://esm.sh/react@18.2.0' import jsxToString from "https://cdn.skypack.dev/jsx-to-string@1.4.0"; // 改为普通类,不继承Function class T { foo; constructor(foo) { this.foo = foo; return new Proxy(this, { apply: (target) => target._call() }); } bar() { return this.foo + 'bar'; } toString() { return this.bar(); } _call() { return this.bar(); } } const a = new T('foo'); const el1 = (<div>{`${a}`}</div>); const el2 = (<div>{a()}</div>); const el3 = (<div>{a}</div>); console.log(jsxToString(el1)) // "<div>foobar</div>" console.log(jsxToString(el2)) // "<div>foobar</div>" console.log(jsxToString(el3)) // "<div>foobar</div>"
方案3:让函数实例作为组件返回文本
如果必须继承Function,可以让实例被当作React组件调用时直接返回目标字符串(React 16+支持组件返回原生字符串/数字):
import React from 'https://esm.sh/react@18.2.0' import jsxToString from "https://cdn.skypack.dev/jsx-to-string@1.4.0"; class T extends Function { foo; constructor(foo) { super(); this.foo = foo; return new Proxy(this, { apply: (target) => target._call() }); } bar() { return this.foo + 'bar'; } toString() { return this.bar(); } // 让组件调用时返回字符串 _call() { return this.bar(); } } const a = new T('foo'); const el1 = (<div>{`${a}`}</div>); const el2 = (<div>{a()}</div>); const el3 = (<div>{a}</div>); console.log(jsxToString(el1)) // "<div>foobar</div>" console.log(jsxToString(el2)) // "<div>foobar</div>" console.log(jsxToString(el3)) // "<div>foobar</div>"
内容的提问来源于stack exchange,提问作者Jesus_21
相关产品推荐
相关产品推荐

