Skip to content
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

Use functional component instead of class-based for template MyComponent #100

Merged
merged 5 commits into from
Dec 4, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 50 additions & 64 deletions template/my_component/frontend/src/MyComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,84 +1,70 @@
import {
Streamlit,
StreamlitComponentBase,
withStreamlitConnection,
ComponentProps,
} from "streamlit-component-lib"
import React, { ReactNode } from "react"

interface State {
numClicks: number
isFocused: boolean
}
import React, { useCallback, useEffect, useMemo, useState, ReactElement } from "react"

/**
* This is a React-based component template. The `render()` function is called
* automatically when your component should be re-rendered.
* This is a React-based component template. The passed props are coming from the
* Streamlit library. Your custom args can be accessed via the `args` props.
*/
class MyComponent extends StreamlitComponentBase<State> {
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 [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 = {}
const style: React.CSSProperties = useMemo(() => {
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"}`
return { 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 (
<span>
Hello, {name}! &nbsp;
<button
style={style}
onClick={this.onClicked}
disabled={this.props.disabled}
onFocus={this._onFocus}
onBlur={this._onBlur}
>
Click Me!
</button>
</span>
)
}
useEffect(() => {
Streamlit.setComponentValue(numClicks)
}, [numClicks])

useEffect(() => {
Streamlit.setFrameHeight()
}, [style, numClicks])
raethlein marked this conversation as resolved.
Show resolved Hide resolved

/** 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 = useCallback((): void => {
setNumClicks((prevNumClicks) => prevNumClicks + 1)
}, [setNumClicks])
sfc-gh-bnisco marked this conversation as resolved.
Show resolved Hide resolved

/** Focus handler for our "Click Me!" button. */
private _onFocus = (): void => {
this.setState({ isFocused: true })
}
const onFocus = useCallback((): void => {
setIsFocused(true)
}, [setIsFocused])

/** Blur handler for our "Click Me!" button. */
private _onBlur = (): void => {
this.setState({ isFocused: false })
}
const onBlur = useCallback((): void => {
setIsFocused(false)
}, [setIsFocused])

// 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 (
<span>
Hello, {name}! &nbsp;
<button
style={style}
onClick={onClicked}
disabled={disabled}
onFocus={onFocus}
onBlur={onBlur}
>
Click Me!
</button>
</span>
)
}

// "withStreamlitConnection" is a wrapper function. It bootstraps the
Expand Down
Loading