-
Notifications
You must be signed in to change notification settings - Fork 572
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Only apply forwardRef for React when props.ref
is passed
#1172
base: main
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
props.ref
is passed
Fixes #1159 |
can you handle the conflicts in this branch and merge main so we see a cleaner diff? |
# Conflicts: # packages/core/src/__tests__/__snapshots__/preact.test.ts.snap # packages/core/src/generators/react/generator.ts
@@ -14,7 +14,8 @@ const prohibitedKeywordRE = new RegExp( | |||
'extends,finally,continue,debugger,function,arguments,typeof,void,' + | |||
// both below conditions added based on https://github.com/vuejs/vue/blob/e80cd09fff570df57d608f8f5aaccee6d7f31917/src/core/instance/state.ts#L89-L96 | |||
// below line added from https://github.com/vuejs/vue/blob/8880b55d52f8d873f79ef67436217c8752cddef5/src/shared/util.ts#L130 | |||
'key,ref,slot,slot-scope,is,' + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is used by many generators, and we need to forbid these reserved words for Vue as per the comment above: https://github.com/vuejs/vue/blob/8880b55d52f8d873f79ef67436217c8752cddef5/src/shared/util.ts#L130
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternative method:
Can we break this into two distinct regex keywords so that they only apply to the specific generator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah.
I'd do that by adding a target
argument to getProps
, and toggle parts of the regex based on the need. Something like:
target === 'react' ? '' : 'ref'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized this function is never used by the React generatore, so I'm not sure your changes are doing anything.
# Conflicts: # packages/core/src/generators/react/generator.ts
☁️ Nx Cloud ReportCI is running/has finished running commands for commit d249c8e. As they complete they will appear below. Click to see the status, the terminal output, and the build insights. 📂 See all runs for this branch ✅ Successfully ran 4 targetsSent with 💌 from NxCloud. |
@@ -14,7 +14,8 @@ const prohibitedKeywordRE = new RegExp( | |||
'extends,finally,continue,debugger,function,arguments,typeof,void,' + | |||
// both below conditions added based on https://github.com/vuejs/vue/blob/e80cd09fff570df57d608f8f5aaccee6d7f31917/src/core/instance/state.ts#L89-L96 | |||
// below line added from https://github.com/vuejs/vue/blob/8880b55d52f8d873f79ef67436217c8752cddef5/src/shared/util.ts#L130 | |||
'key,ref,slot,slot-scope,is,' + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized this function is never used by the React generatore, so I'm not sure your changes are doing anything.
traverse(json).forEach(function (item) { | ||
if (isMitosisNode(item)) { | ||
const binding = item.bindings.ref; | ||
const regexp = /(.+)?props\.(ref)( |\)|;|\()?$/; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want to avoid adding new regexes to the codebase moving forward and rely instead on Babel transformers. Regexes are very error-prone and brittle, and hard to understand. I can tell you copy/pasted similar helpers used by other generators but this stuff causes lots of bugs.
To use Babel, you can rely on https://astexplorer.net/ to figure out how the AST is structured, or look at some examples in the codebase:
mitosis/packages/core/src/helpers/map-refs.ts
Lines 13 to 100 in d5feb30
const replaceRefsInString = (code: string, refs: string[], mapper: RefMapper) => { | |
return babelTransformExpression(code, { | |
Identifier(path: NodePath<types.Identifier>) { | |
const name = path.node.name; | |
const isRef = refs.includes(name); | |
if (isRef) { | |
path.replaceWith(types.identifier(mapper(name))); | |
} | |
}, | |
}); | |
}; | |
export const mapRefs = (component: MitosisComponent, mapper: RefMapper): void => { | |
const refSet = getRefs(component); | |
// grab refs not used for bindings | |
Object.keys(component.refs).forEach((ref) => refSet.add(ref)); | |
const refs = Array.from(refSet); | |
for (const key of Object.keys(component.state)) { | |
const stateVal = component.state[key]; | |
if (typeof stateVal?.code === 'string') { | |
const value = stateVal.code; | |
switch (stateVal.type) { | |
case 'method': | |
case 'getter': | |
const isGet = stateVal.type === 'getter'; | |
const isSet = Boolean(value.match(SETTER)); | |
component.state[key] = { | |
code: replaceRefsInString( | |
value.replace(/^(get |set )?/, 'function '), | |
refs, | |
mapper, | |
).replace(/^function /, isGet ? 'get ' : isSet ? 'set ' : ''), | |
type: stateVal.type, | |
}; | |
break; | |
case 'function': | |
component.state[key] = { | |
code: replaceRefsInString(value, refs, mapper), | |
type: 'function', | |
}; | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
traverse(component).forEach(function (item) { | |
if (isMitosisNode(item)) { | |
for (const key of Object.keys(item.bindings)) { | |
const value = item.bindings[key]; | |
if (typeof value === 'object' && key !== 'ref') { | |
item.bindings[key] = { | |
...value, | |
code: replaceRefsInString(value.code as string, refs, mapper), | |
}; | |
} | |
} | |
} | |
}); | |
for (const key of Object.keys(component.hooks) as (keyof typeof component.hooks)[]) { | |
const hooks = component.hooks[key]; | |
if (Array.isArray(hooks)) { | |
hooks.forEach((hook) => { | |
if (hook.code) { | |
hook.code = replaceRefsInString(hook.code, refs, mapper); | |
} | |
if (hook.deps) { | |
hook.deps = replaceRefsInString(hook.deps, refs, mapper); | |
} | |
}); | |
} else { | |
const hookCode = hooks?.code; | |
if (hookCode) { | |
hooks.code = replaceRefsInString(hookCode, refs, mapper); | |
} | |
if (hooks?.deps) { | |
hooks.deps = replaceRefsInString(hooks?.deps, refs, mapper); | |
} | |
} | |
} | |
}; |
I made a quick example that should do what you want here: https://astexplorer.net/#/gist/5b7fcfe92b39eb399aea7236758befa2/latest
You can reuse babelTransformExpression
to run the transform on the code
Description
This PR ensures that
forwardRef
s only get passed in React when:-
props.ref
is passed-
useMetadata
has a passed name that matches a ref