bug fix: inputs resetting if a websocket messages triggers a render

main
alex 2024-03-03 18:23:13 +01:00
parent ddd86ed479
commit a3a63b8d84
5 changed files with 260 additions and 9 deletions

View File

@ -1,4 +1,5 @@
import { createContext, useContext, useRef, useState } from "react"; import { createContext, useContext, useRef, useState } from "react";
import { Form } from "antd";
const preview = { const preview = {
categoryGroup: {}, categoryGroup: {},
@ -10,6 +11,7 @@ const preview = {
previousParamCategory: null, previousParamCategory: null,
paginationPageRef: null, paginationPageRef: null,
selectInputs: {}, selectInputs: {},
form: null,
}; };
const GroupTasksContext = createContext(preview); const GroupTasksContext = createContext(preview);
@ -32,6 +34,8 @@ export function GroupTasksProvider({ children }) {
// this is used for the <Select /> inputs as there is no way to manipulate the select value via the DOM (like we do on the text inputs) as it is needed by the websocket // this is used for the <Select /> inputs as there is no way to manipulate the select value via the DOM (like we do on the text inputs) as it is needed by the websocket
const [selectInputs, setSelectInputs] = useState({}); const [selectInputs, setSelectInputs] = useState({});
const [form] = Form.useForm();
return ( return (
<GroupTasksContext.Provider <GroupTasksContext.Provider
value={{ value={{
@ -50,6 +54,7 @@ export function GroupTasksProvider({ children }) {
paginationPageRef, paginationPageRef,
selectInputs, selectInputs,
setSelectInputs, setSelectInputs,
form,
}} }}
> >
{children} {children}

View File

@ -294,13 +294,15 @@ export function handleWebSocketMessage(
// update input value // update input value
if (body.inputType === "text") { if (body.inputType === "text") {
groupTasksContext.form.setFieldValue(body.element, body.value);
// html based DOM manipulation // html based DOM manipulation
const foundInput = document.getElementById(body.element); /*const foundInput = document.getElementById(body.element);
if (foundInput) { if (foundInput) {
// this timeout is needed because the previous useState for the lockedByUserId takes some milliseconds to complete // this timeout is needed because the previous useState for the lockedByUserId takes some milliseconds to complete
setTimeout(() => setNativeValue(foundInput, body.value), 50); // setTimeout(() => setNativeValue(foundInput, body.value), 50);
} } */
} else if (body.inputType === "select" || body.inputType === "checkbox") { } else if (body.inputType === "select" || body.inputType === "checkbox") {
groupTasksContext.setSelectInputs((prev) => { groupTasksContext.setSelectInputs((prev) => {
const newInputs = { ...prev }; const newInputs = { ...prev };
@ -1106,7 +1108,11 @@ function setNativeValue(element, value) {
// React 16 // React 16
let tracker = element._valueTracker; let tracker = element._valueTracker;
if (tracker) { if (tracker) {
console.log("tracker set value", value);
console.log(element);
tracker.setValue(lastValue); tracker.setValue(lastValue);
} else {
console.log("tracker not set value");
} }
element.dispatchEvent(event); element.dispatchEvent(event);
} }

View File

@ -389,6 +389,8 @@ export default function GroupTasksViewModal({ isOpen }) {
groupTaskSteps.sort((a, b) => a.Step - b.Step); groupTaskSteps.sort((a, b) => a.Step - b.Step);
if (groupTasksContext.categoryGroup.groups === undefined) return [];
groupTasksContext.categoryGroup.groups.forEach((group) => { groupTasksContext.categoryGroup.groups.forEach((group) => {
if (currentGroupTask.current.GroupId === group.id) { if (currentGroupTask.current.GroupId === group.id) {
groupTasks = group.tasks; groupTasks = group.tasks;
@ -770,9 +772,9 @@ function InputRequiredHandler({
currentGroupTask, currentGroupTask,
groupTaskParameters, groupTaskParameters,
groupTaskStepInputs, groupTaskStepInputs,
notificationApi,
step, step,
taskLockedByUserId, taskLockedByUserId,
notificationApi,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const groupTasksContext = useGroupTasksContext(); const groupTasksContext = useGroupTasksContext();
@ -860,7 +862,236 @@ function InputRequiredHandler({
typingTimer.current = setTimeout(() => typingMessage(), 1000); typingTimer.current = setTimeout(() => typingMessage(), 1000);
}; };
const getInitialFormValues = () => {
let initialValues = {};
groupTaskParameters.forEach((groupTaskParameter) => {
initialValues[
`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`
] = getDefaultValue(groupTaskParameter);
});
return initialValues;
};
return ( return (
<Form
form={groupTasksContext.form}
layout="vertical"
id="groupTasksViewModalRequiredInputsForm"
initialValues={getInitialFormValues()}
>
{groupTaskParameters.map((groupTaskParameter, index) => {
switch (groupTaskParameter.type) {
case "text":
return (
<MyFormItem
key={index}
t={t}
labelDisplayName={groupTaskParameter.displayName}
isGlobal={groupTaskParameter.global}
name={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
>
<Input
id={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
disabled={taskLockedByUserId !== ""}
onChange={(e) =>
handleInputChange(
"text",
e.target.value,
currentGroupTask.Id,
groupTaskParameter.parameterName
)
}
/>
</MyFormItem>
);
case "number":
return (
<MyFormItem
key={index}
t={t}
labelDisplayName={groupTaskParameter.displayName}
isGlobal={groupTaskParameter.global}
name={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
>
<InputNumber
id={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
style={{ width: "100%" }}
disabled={taskLockedByUserId !== ""}
max={Number.MAX_SAFE_INTEGER}
onChange={(e) =>
handleInputChange(
"text",
e,
currentGroupTask.Id,
groupTaskParameter.parameterName
)
}
/>
</MyFormItem>
);
case "textarea":
return (
<MyFormItem
key={index}
t={t}
labelDisplayName={groupTaskParameter.displayName}
isGlobal={groupTaskParameter.global}
name={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
>
<TextArea
id={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
disabled={taskLockedByUserId !== ""}
onChange={(e) =>
handleInputChange(
"text",
e.target.value,
currentGroupTask.Id,
groupTaskParameter.parameterName
)
}
/>
</MyFormItem>
);
case "select":
return (
<MyFormItem
key={index}
t={t}
labelDisplayName={groupTaskParameter.displayName}
isGlobal={groupTaskParameter.global}
name={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
>
<SelectComponent
id={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
t={t}
disabled={taskLockedByUserId !== ""}
options={groupTaskParameter.options}
parameterName={groupTaskParameter.parameterName}
onSelectChange={(value) =>
handleInputChange(
"select",
value,
currentGroupTask.Id,
groupTaskParameter.parameterName
)
}
/>
</MyFormItem>
);
case "select_machine":
return (
<MyFormItem
key={index}
t={t}
labelDisplayName={groupTaskParameter.displayName}
isGlobal={groupTaskParameter.global}
>
<SelectMachineComponent
id={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
disabled={taskLockedByUserId !== ""}
t={t}
notificationApi={notificationApi}
globalInput={groupTaskParameter}
onSelectChange={(value, notes) =>
handleInputChange(
"select",
value,
currentGroupTask.Id,
groupTaskParameter.parameterName,
notes
)
}
/>
</MyFormItem>
);
case "checkbox":
return (
<MyFormItem
key={index}
t={t}
style={{
marginBottom:
groupTaskParameters.length > index &&
groupTaskParameters[index + 1].type === "checkbox"
? 0
: AppStyle.app.margin,
}}
name={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
>
<Checkbox
id={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
disabled={taskLockedByUserId !== ""}
checked={
groupTasksContext.selectInputs[
groupTaskParameter.parameterName
]?.value
}
onChange={(e) => {
groupTasksContext.setSelectInputs((prevState) => ({
...prevState,
[groupTaskParameter.parameterName]: {
value: e.target.checked,
},
}));
handleInputChange(
"checkbox",
e.target.checked,
currentGroupTask.Id,
groupTaskParameter.parameterName
);
}}
>
<MyFormItemLabel
t={t}
displayName={groupTaskParameter.displayName}
isGlobal={groupTaskParameter.global}
/>
</Checkbox>
</MyFormItem>
);
case "select":
return (
<MyFormItem
key={index}
t={t}
labelDisplayName={groupTaskParameter.displayName}
isGlobal={groupTaskParameter.global}
name={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
>
<SelectComponent
id={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
t={t}
disabled={taskLockedByUserId !== ""}
options={groupTaskParameter.options}
parameterName={groupTaskParameter.parameterName}
onSelectChange={(value) =>
handleInputChange(
"select",
value,
currentGroupTask.Id,
groupTaskParameter.parameterName
)
}
/>
</MyFormItem>
);
default:
return (
<Typography.Text type="danger" key={index}>
Type <b>{groupTaskParameter.type}</b> not implemented. Was
specified in: <b>{groupTaskParameter.displayName}</b> <br />
<br />
</Typography.Text>
);
}
})}
</Form>
);
/*return (
<Form layout="vertical" id="groupTasksViewModalRequiredInputsForm"> <Form layout="vertical" id="groupTasksViewModalRequiredInputsForm">
{groupTaskParameters.map((groupTaskParameter, index) => { {groupTaskParameters.map((groupTaskParameter, index) => {
switch (groupTaskParameter.type) { switch (groupTaskParameter.type) {
@ -1043,7 +1274,7 @@ function InputRequiredHandler({
} }
})} })}
</Form> </Form>
); ); */
} }
function GroupTaskStepLogHandler({ currentGroupTaskId, log, files }) { function GroupTaskStepLogHandler({ currentGroupTaskId, log, files }) {

View File

@ -181,11 +181,19 @@ export default function GroupTasks({ isGroupTasksViewModalOpen }) {
); );
} }
export function MyFormItem({ children, t, style, labelDisplayName, isGlobal }) { export function MyFormItem({
children,
t,
style,
labelDisplayName,
isGlobal,
name,
}) {
return ( return (
<Form.Item <Form.Item
style={style} style={style}
required required
name={name}
label={ label={
labelDisplayName !== undefined && ( labelDisplayName !== undefined && (
<MyFormItemLabel <MyFormItemLabel

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { Suspense } from "react";
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client";
import "./index.css"; import "./index.css";
import App from "./App"; import App from "./App";
@ -24,12 +24,13 @@ const Loading = () => {
*/ */
const root = ReactDOM.createRoot(document.getElementById("root")); const root = ReactDOM.createRoot(document.getElementById("root"));
root.render( root.render(
<React.Suspense fallback={<Loading />}> <Suspense fallback={<Loading />}>
<BrowserRouter> <BrowserRouter>
<App /> <App />
</BrowserRouter> </BrowserRouter>
</React.Suspense> </Suspense>
); );
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function