mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-06-05 21:20:40 +02:00
frontend: using reducer to render components
This commit is contained in:
parent
e7350c9ec0
commit
7fe2cc016d
@ -1,13 +1,14 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import { useEffect, useReducer } from 'react';
|
||||||
import { Component, ComponentLabel } from './components/component';
|
import { Component, ComponentLabel } from './components/component';
|
||||||
import { ButtonComponent } from './components/button';
|
import { ButtonComponent } from './components/button';
|
||||||
import { socket } from './socket';
|
import { socket } from './socket';
|
||||||
|
|
||||||
type AttributeType = 'str' | 'bool' | 'float' | 'int' | 'method' | 'Subclass';
|
type AttributeType = 'str' | 'bool' | 'float' | 'int' | 'method' | 'Subclass';
|
||||||
|
|
||||||
|
type ValueType = boolean | string | number | object;
|
||||||
interface Attribute {
|
interface Attribute {
|
||||||
type: AttributeType;
|
type: AttributeType;
|
||||||
value?: any;
|
value?: ValueType;
|
||||||
readonly: boolean;
|
readonly: boolean;
|
||||||
doc?: string | null;
|
doc?: string | null;
|
||||||
parameters?: Record<string, string>;
|
parameters?: Record<string, string>;
|
||||||
@ -15,46 +16,104 @@ interface Attribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MyData = Record<string, Attribute>;
|
type MyData = Record<string, Attribute>;
|
||||||
|
type State = MyData | null;
|
||||||
|
type Action =
|
||||||
|
| { type: 'SET_DATA'; data: MyData }
|
||||||
|
| { type: 'UPDATE_ATTRIBUTE'; parent_path: string; name: string; value: ValueType };
|
||||||
|
type NotificationElement = {
|
||||||
|
data: { parent_path: string; name: string; value: object };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function to update a specific property in a deeply nested object.
|
||||||
|
* The property to be updated is specified by a path array.
|
||||||
|
*
|
||||||
|
* @param {Array<string>} path - An array where each element is a key in the object,
|
||||||
|
* forming a path to the property to be updated.
|
||||||
|
* @param {object} obj - The object to be updated.
|
||||||
|
* @param {object} value - The new value for the property specified by the path.
|
||||||
|
* @return {object} - A new object with the specified property updated.
|
||||||
|
*/
|
||||||
|
function updateNestedObject(path: Array<string>, obj: object, value: ValueType) {
|
||||||
|
// Base case: If the path is empty, return the new value.
|
||||||
|
// This means we've reached the nested property to be updated.
|
||||||
|
if (path.length === 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursive case: If the path is not empty, split it into the first key and the rest
|
||||||
|
// of the path.
|
||||||
|
const [first, ...rest] = path;
|
||||||
|
|
||||||
|
// Return a new object that copies all properties of the original object, but updates
|
||||||
|
// the property specified by 'first'.
|
||||||
|
// The updated property is an object that copies all properties of the original nested
|
||||||
|
// object, but updates the 'value' property.
|
||||||
|
// The new 'value' property is the result of a recursive call to updateNestedObject,
|
||||||
|
// with the rest of the path, the value of the nested object as the object to be
|
||||||
|
// updated, and the new value.
|
||||||
|
return {
|
||||||
|
...obj,
|
||||||
|
[first]: {
|
||||||
|
...obj[first],
|
||||||
|
value: updateNestedObject(rest, obj[first]?.value || {}, value)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const reducer = (state: State, action: Action): State => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'SET_DATA':
|
||||||
|
return action.data;
|
||||||
|
case 'UPDATE_ATTRIBUTE':
|
||||||
|
if (!state) {
|
||||||
|
console.log('No state is set');
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const path = action.parent_path.split('.').slice(1).concat(action.name);
|
||||||
|
console.log(path);
|
||||||
|
|
||||||
|
return updateNestedObject(path, state, action.value);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [data, setData] = useState<MyData | null>(null);
|
const [state, dispatch] = useReducer(reducer, null);
|
||||||
const [isConnected, setIsConnected] = useState(socket.connected);
|
// const [isConnected, setIsConnected] = useState(socket.connected);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Fetch data from the API when the component mounts
|
// Fetch data from the API when the component mounts
|
||||||
fetch('http://localhost:8001/service-properties')
|
fetch('http://localhost:8001/service-properties')
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then(setData);
|
.then((data: MyData) => dispatch({ type: 'SET_DATA', data }));
|
||||||
function onConnect() {
|
|
||||||
setIsConnected(true);
|
function onNotify(value: NotificationElement) {
|
||||||
|
dispatch({
|
||||||
|
type: 'UPDATE_ATTRIBUTE',
|
||||||
|
parent_path: value.data.parent_path,
|
||||||
|
name: value.data.name,
|
||||||
|
value: value.data.value
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDisconnect() {
|
|
||||||
setIsConnected(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onNotify(value: Record<string, any>) {
|
|
||||||
console.log(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.on('connect', onConnect);
|
|
||||||
socket.on('disconnect', onDisconnect);
|
|
||||||
socket.on('notify', onNotify);
|
socket.on('notify', onNotify);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
socket.off('connect', onConnect);
|
|
||||||
socket.off('disconnect', onDisconnect);
|
|
||||||
socket.off('notify', onNotify);
|
socket.off('notify', onNotify);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// While the data is loading
|
// While the data is loading
|
||||||
if (!data) {
|
if (!state) {
|
||||||
return <p>Loading...</p>;
|
return <p>Loading...</p>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
{Object.entries(data).map(([key, value]) => {
|
{Object.entries(state).map(([key, value]) => {
|
||||||
if (value.type === 'bool') {
|
if (value.type === 'bool') {
|
||||||
return (
|
return (
|
||||||
<div key={key}>
|
<div key={key}>
|
||||||
@ -62,7 +121,7 @@ const App = () => {
|
|||||||
name={key}
|
name={key}
|
||||||
docString={value.doc}
|
docString={value.doc}
|
||||||
readOnly={value.readonly}
|
readOnly={value.readonly}
|
||||||
value={value.value}
|
value={Boolean(value.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user