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)