-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
refactor(ui): workflow panel components from class to functional #11803
refactor(ui): workflow panel components from class to functional #11803
Conversation
Sorry, I added a commit. |
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.
Added comment, but I confirmed no regressions.
const [entrypoint, setEntrypoint] = useState<string>(workflowEntrypoint); | ||
const [parameters, setParameters] = useState<Parameter[]>([]); | ||
const [workflowParameters, setWorkflowParameters] = useState<Parameter[]>(JSON.parse(JSON.stringify(props.workflowParameters))); | ||
const [templates] = useState<Template[]>([defaultTemplate].concat(props.templates)); |
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.
Originally state is used, but should we change here without useState?
const templates = [defaultTemplate].concat(props.templates);
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.
Yea this doesn't seem like it actually uses state, just derives.
If that was meant to be a performance optimization, useMemo
should be used instead (I believe there's a few places in the code where it seemed like it might be more appropriate for useMemo
).
I left out most performance optimizations in my refactor PRs
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 changed templates
from state to const and memoized the select options created based on templates
Could you review if the changes are appropriate?
9050132
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 think the memoization might not work as you're comparing templates
, which is an array. useMemo
uses Object.is
to check equality of the dependencies, which uses reference equality for objects (and arrays). So it will probably get recalculated every time as templates
is recalculated it above it every time (and therefore has a new reference each time).
I'm fine with no useMemo
and just having it be a normal variable, as the performance difference is not really significant. Otherwise can try using props.templates
as the dependency instead (as templates
is wholly derived from props.templates
, they are semantically equivalent dependencies), which I think would pass reference equality checks as it does not change after the initial fetch of the WorkflowTemplate
. Along the same lines, if you use useMemo
on the templates
variable as well, that should also suffice for reference equality (as templates
's reference would no longer change then)
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.
thanks for detailed explanation on useMemo!
I now have a better understanding. (I didn't understand it before😃)
I fixed it and confirmed that variables templates and templatesOptions are not recalculated using console.log.
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.
Glad to help 🙂
New version looks correct to me!
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.
mostly looks good to me, though I've requested some further improvements in-line. basically to use named functions, async/await, optional chaining, type inference, and some small optimizations
memoized: false, | ||
isSubmitting: false, | ||
overrideParameters: false | ||
const submit = () => { |
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.
const submit = () => { | |
function submit() { |
prefer named function over const assigned to anonymous function (it's more performant and easier to trace in debug logs etc since "anonymous" is not the name -- I also used this in all of my recent PRs)
.then(handleSuccess) | ||
.catch(handleError); | ||
} | ||
}; |
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.
}; | |
} |
once it's a named func, the semi-colon will be auto removed by lint
const handleSuccess = (submitted: Workflow) => { | ||
document.location.href = uiUrl(`workflows/${submitted.metadata.namespace}/${submitted.metadata.name}`); | ||
}; |
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.
const handleSuccess = (submitted: Workflow) => { | |
document.location.href = uiUrl(`workflows/${submitted.metadata.namespace}/${submitted.metadata.name}`); | |
}; | |
function handleSuccess(submitted: Workflow) { | |
document.location.href = uiUrl(`workflows/${submitted.metadata.namespace}/${submitted.metadata.name}`); | |
} |
same as above.
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.
or this gets removed entirely if submit
is rewritten as it gets in-lined
</div> | ||
</div> | ||
const handleSuccess = (submitted: Workflow) => { | ||
document.location.href = uiUrl(`workflows/${submitted.metadata.namespace}/${submitted.metadata.name}`); |
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 did not change, but shouldn't this use react-router navigation instead?
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.
Ah I think the current pattern is to use the History API with history.push
. We are on an older version of react-router though (I'm working on upgrading it -- have to finish removing BasePage
first), so I don't think the useHistory
hook is available. Other components use history
from props currently
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.
thanks! I was just researching how to write in current react router version.
I'll try!
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 tryed passing history
in props and replaced 'document.location.href' with 'history.push', but it didn't work as I expected :(
props.history.push(`workflows/${submitted.metadata.namespace}/${submitted.metadata.name}`);
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.
If the scope of the changes is likely to be larger than initially expected, it might be good to reconsider the approach after your react-router version up I think.
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.
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.
Huh. Since this is on the same page (just different Workflow), I wonder if maybe the page is not quite unloading correctly and still has the old state. The effects seem to have proper disposers and dependencies, so I don't see anything strange off the top of my head.
At this point, especially since it potentially involves a whole other page's logic, I think we should punt on the navigation improvements for now. Appreciate your efforts and investigation here! Hopefully that will help the next person to tackle this too (which could be yourself 😉 ).
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.
In the retry component, the state appears to be changing as expected, so the difference in implementation between retry and resubmit may be a clue, though I haven't been able to found out.
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.
If I figure it out, I'll make a PR🔥
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.
so the difference in implementation between retry and resubmit may be a clue
So a retry does not create a new Workflow, it just reruns failed steps. A resubmit creates a wholly new Workflow, effectively a clone of the old one.
So for a retry, the URL does not change. For a resubmit it does.
For the retry, the existing ListWatch
might be able to pick up the change automatically. For a resubmit, an entirely new request is needed for the new Workflow.
Maybe those details may help you debug?
I already knew that though (I wrote a bit of the documentation on retry vs resubmit in #11625) and couldn't figure it out, though I didn't stare too hard
const handleError = (err: Error) => { | ||
setError(err); | ||
setIsSubmitting(false); | ||
}; |
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.
const handleError = (err: Error) => { | |
setError(err); | |
setIsSubmitting(false); | |
}; | |
function handleError(err: Error) { | |
setError(err); | |
setIsSubmitting(false); | |
} |
same as above.
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.
or this gets removed entirely if submit
is rewritten as it gets in-lined
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 reverted handleSuccess and handleError funcs to inline because there was no need to extract functions by using await/async and try/catch as you pointed out. thanks!
const getSelectedTemplate = (name: string): Template | null => { | ||
return templates.find(t => t.name === name) || null; | ||
}; |
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.
const getSelectedTemplate = (name: string): Template | null => { | |
return templates.find(t => t.name === name) || null; | |
}; | |
function getSelectedTemplate(name: string): Template | null { | |
return templates.find(t => t.name === name) || null; | |
} |
same as above re: named functions over anonymous
onChange={selected => { | ||
const selectedTemp = getSelectedTemplate(selected.value); | ||
setEntrypoint(selected.value); | ||
setParameters((selectedTemp && selectedTemp.inputs.parameters) || []); |
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.
setParameters((selectedTemp && selectedTemp.inputs.parameters) || []); | |
setParameters(selectedTemp?.inputs?.parameters || []); |
can simplify this with optional chaining syntax
const submit = () => { | ||
setIsSubmitting(true); | ||
services.workflows | ||
.submit(this.props.kind, this.props.name, this.props.namespace, { | ||
entryPoint: this.state.entrypoint === workflowEntrypoint ? null : this.state.entrypoint, | ||
.submit(props.kind, props.name, props.namespace, { | ||
entryPoint: entrypoint === workflowEntrypoint ? null : entrypoint, | ||
parameters: [ | ||
...this.state.workflowParameters.filter(p => Utils.getValueFromParameter(p) !== undefined).map(p => p.name + '=' + Utils.getValueFromParameter(p)), | ||
...this.state.parameters.filter(p => Utils.getValueFromParameter(p) !== undefined).map(p => p.name + '=' + Utils.getValueFromParameter(p)) | ||
...workflowParameters.filter(p => Utils.getValueFromParameter(p) !== undefined).map(p => p.name + '=' + Utils.getValueFromParameter(p)), | ||
...parameters.filter(p => Utils.getValueFromParameter(p) !== undefined).map(p => p.name + '=' + Utils.getValueFromParameter(p)) | ||
], | ||
labels: this.state.labels.join(',') | ||
labels: labels.join(',') | ||
}) | ||
.then((submitted: Workflow) => (document.location.href = uiUrl(`workflows/${submitted.metadata.namespace}/${submitted.metadata.name}`))) | ||
.catch(error => this.setState({error, isSubmitting: false})); | ||
} | ||
.catch(err => { | ||
setError(err); | ||
setIsSubmitting(false); | ||
}); | ||
}; |
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.
similar to above, could we convert this to async/await and a named function as well?
], | ||
labels: this.state.labels.join(',') | ||
labels: labels.join(',') | ||
}) | ||
.then((submitted: Workflow) => (document.location.href = uiUrl(`workflows/${submitted.metadata.namespace}/${submitted.metadata.name}`))) |
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 hasn't changed, but also should perhaps use react-router navigation instead?
Most of the PR description verbatim copy+pastes my previous PRs (e.g. #11800). That's fine, but please cite your sources, especially when it is verbatim. |
Would either of you like to review the other PRs I have open for the refactor? Could help speed up getting them merged 🙂 |
Signed-off-by: tetora1053 <[email protected]>
Signed-off-by: tetora1053 <[email protected]>
Signed-off-by: tetora1053 <[email protected]>
Signed-off-by: tetora1053 <[email protected]>
Signed-off-by: tetora1053 <[email protected]>
Signed-off-by: tetora1053 <[email protected]>
Signed-off-by: tetora1053 <[email protected]>
Signed-off-by: tetora1053 <[email protected]>
Signed-off-by: tetora1053 <[email protected]>
e370c67
to
0e47525
Compare
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.
Since we're punting on navigation improvements for now, the one remaining thing is the memoization fixes
Signed-off-by: tetora1053 <[email protected]>
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.
LGTM. Thanks for getting the refactors, iteration, and performance improvements done!
@agilgur5 having my pull request merged means a lot to me. and thank you a lot @toyamagu-2021 for passing this issue to me! |
Thanks for your heartfelt words @tetora1053! ❤️ Experiences like these are one of the reasons I continue to work in OSS 🙂 Glad I could help make it a great experience for you and hope to see more of your contributions! Big congrats on your first contribution 🎉 🎉 Thanks @toyamagu-2021 for getting more folks involved as well! |
Partial fix for #9810
Partial fix for #11740
Motivation
Modifications
this.state
->useState
this.props
->props
Verification
Mostly no real semantic changes
manually tested in local environment
submit panel
References PR