@okld @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)