@synode @randyzwitch
Hello,
Thanks for sharing this code which I found useful on two counts: using React (my first time) and the Streamlit component wrapper with property passing and eventing (I hadn’t understood how that works until I understood React’s component rendering life cycle).
I’m using the React app to forward an API call to a service (currently a simple Flask app with a /api/ping endpoint). In the Streamlit app browser dev tools I can see the ping return data, but finding it impossible to return the data in the fetch (tried axios too), and so can’t send this data back to Streamlit.
Here’s the React code. I think I’ve set all CORS headers correctly in the fetch(). Do you have any ideas? (There is a React proxy forwarding URL in package.json for the /api/ping API call.)
import React, { useState, useEffect } from 'react';
import './App.css';
import {
withStreamlitConnection,
ComponentProps,
Streamlit,
} from "streamlit-component-lib";
let numClicks = 0
let hostname = 'No Name'
let action = 'Go!'
let message = 'No Message'
function PingApp(props: ComponentProps) {
if ('hostname' in props.args) {
hostname = props.args.hostname
delete props.args.hostname
}
if ('action' in props.args) {
action = props.args.action
delete props.args.action
}
if ('initial_state' in props.args) {
message = props.args['initial_state']['message']
delete props.args.initial_state
}
if ("events" in props.args) {
props.args.events.forEach((event: string) => {
props.args[event] = (data?: any) => {
Streamlit.setComponentValue({
name: event,
data: data
})
}
})
delete props.args.events
}
useEffect(() => {
Streamlit.setFrameHeight()
})
const [state, setState] = useState({hostname: hostname, numClicks: numClicks, message: message, isError: false, error: ''})
const [ping, setPing] = useState('No Ping')
const [clicks, setClicks] = useState(numClicks)
useEffect(() => {
// https://javascript.info/async
fetch('/api/ping', {
credentials: 'same-origin',
mode: 'no-cors',
headers: {
'Accept':'text/plain',
'Content-Type': 'text/plain',
'Access-Control-Allow-Origin': '*'
}
})
.then(resp => resp.text())
.then(data => setPing(data))
message = ping + '(' + numClicks +')'
}, [clicks, ping]);
useEffect(() => {
setState({hostname: hostname, numClicks: numClicks, message: message, isError: false, error: ''})
}, [clicks, ping])
useEffect(() => {
Streamlit.setComponentValue({name: 'onStatusUpdate', data: state})
}, [state])
const handleClick = async () => {
setClicks(numClicks += 1)
}
return (
<div className="App">
<header className="App-header">
<div>
<span>App Host: {hostname}</span>
</div>
<button onClick={handleClick}>{action}</button>
<div>
<span>Ping Message: {state.message}</span>
</div>
</header>
</div>
);
}
export default withStreamlitConnection(PingApp)