From b15ddec9e1d8640087b081b41ef1c8ed16dc63f3 Mon Sep 17 00:00:00 2001 From: raethlein Date: Wed, 4 Dec 2024 23:52:39 +0100 Subject: [PATCH] Use functional component instead of class-based --- .../my_component/frontend/src/MyComponent.tsx | 101 ++++++++---------- 1 file changed, 42 insertions(+), 59 deletions(-) diff --git a/template/my_component/frontend/src/MyComponent.tsx b/template/my_component/frontend/src/MyComponent.tsx index f68b04f1..2a372eb5 100644 --- a/template/my_component/frontend/src/MyComponent.tsx +++ b/template/my_component/frontend/src/MyComponent.tsx @@ -1,84 +1,67 @@ import { Streamlit, - StreamlitComponentBase, withStreamlitConnection, + ComponentProps, } from "streamlit-component-lib" -import React, { ReactNode } from "react" - -interface State { - numClicks: number - isFocused: boolean -} +import React, { useEffect, useState, ReactElement } from "react" /** * This is a React-based component template. The `render()` function is called * automatically when your component should be re-rendered. */ -class MyComponent extends StreamlitComponentBase { - public state = { numClicks: 0, isFocused: false } +function MyComponent({ args, disabled, theme }: ComponentProps): ReactElement { + const { name } = args - public render = (): ReactNode => { - // Arguments that are passed to the plugin in Python are accessible - // via `this.props.args`. Here, we access the "name" arg. - const name = this.props.args["name"] + const [isFocused, setIsFocused] = useState(false) + const [style, setStyle] = useState({}) + const [numClicks, setNumClicks] = useState(0) - // Streamlit sends us a theme object via props that we can use to ensure - // that our component has visuals that match the active theme in a - // streamlit app. - const { theme } = this.props - const style: React.CSSProperties = {} + useEffect(() => { + if (!theme) return - // Maintain compatibility with older versions of Streamlit that don't send - // a theme object. - if (theme) { - // Use the theme object to style our button border. Alternatively, the - // theme style is defined in CSS vars. - const borderStyling = `1px solid ${ - this.state.isFocused ? theme.primaryColor : "gray" - }` - style.border = borderStyling - style.outline = borderStyling - } + // Use the theme object to style our button border. Alternatively, the + // theme style is defined in CSS vars. + const borderStyling = `1px solid ${isFocused ? theme.primaryColor : "gray"}` + setStyle({ border: borderStyling, outline: borderStyling }) + }, [theme, isFocused]) - // Show a button and some text. - // When the button is clicked, we'll increment our "numClicks" state - // variable, and send its new value back to Streamlit, where it'll - // be available to the Python program. - return ( - - Hello, {name}!   - - - ) - } + useEffect(() => { + Streamlit.setComponentValue(numClicks) + }, [numClicks]) /** Click handler for our "Click Me!" button. */ - private onClicked = (): void => { - // Increment state.numClicks, and pass the new value back to - // Streamlit via `Streamlit.setComponentValue`. - this.setState( - prevState => ({ numClicks: prevState.numClicks + 1 }), - () => Streamlit.setComponentValue(this.state.numClicks) - ) + const onClicked = (): void => { + setNumClicks((prevNumClicks) => prevNumClicks + 1) } /** Focus handler for our "Click Me!" button. */ - private _onFocus = (): void => { - this.setState({ isFocused: true }) + const onFocus = (): void => { + setIsFocused(true) } /** Blur handler for our "Click Me!" button. */ - private _onBlur = (): void => { - this.setState({ isFocused: false }) + const onBlur = (): void => { + setIsFocused(false) } + + // Show a button and some text. + // When the button is clicked, we'll increment our "numClicks" state + // variable, and send its new value back to Streamlit, where it'll + // be available to the Python program. + return ( + + Hello, {name}!   + + + ) } // "withStreamlitConnection" is a wrapper function. It bootstraps the