Communicating with a React Component from Streamlit AFTER the component has been created?

Hi,

Just wondering if communicating with a React Component from Streamlit AFTER the component has been created is possible?

component_host = components.declare_component(name='ComponentHost', url='http://localhost:3001')
props = {'data': 'initialValue'}
event = component_host(key='id1', **props)
newValue = processEvent(event)

Itā€™s no problem receiving events from the component but I canā€™t seem to round trip the processed event ā€˜newValueā€™ back to my component via my component_host reference.

Any ideas? Do I need to post ā€˜nextValueā€™ to a REST endpoint hosted in my React component instead (and use global state like Redux to share that value between the endpoint and component)? This seems convoluted given I think I have a channel already in component_host.

Thanks.

Hey @asehmi,

Let me make sure Iā€™m understanding the question:

event = component_host(key='id1', **props)
newValue = processEvent(event)
# You want to send newValue back to component_host?

Assuming this is the case, the answer is that it canā€™t be done particularly easily. You can effectively communicate ā€œonceā€ with your component each time your script runs. (That is, you can invoke the component - which instantiates it, sends its parameters to the frontend, and retrieves the latest value returned from the frontend - once per run.)

If you want to do component_host(new_value), you could store new_value in a SessionState object, and retrieve it at the start of your app script. (And you can force your script to re-run by using the st.experimental_rerun API.)

But this is a bit awkward, and I wonder if thereā€™s a better way to accomplish what youā€™re looking for. Can you describe your use case and data flow in more detail?

1 Like

Thanks for a quick response. This is all about trying to overcome the allow-same-origin problem with components that need to make an API web request. What I ended up doing was getting the component to delegate the API web request back to Streamlit, and now I want to send those API results back to the component. Ultimately, what I want to do is integrate authentication with Auth0 identity provider, store API access tokens I receive in a remote DB and invoke APIs using said access tokens. Since components currently donā€™t allow API calls, Iā€™m trying these workarounds.

I have an Auth0 login service (Flask App) integrated using an iframe in Streamlit. After the authentication callback completes, I display a code to the user who cuts and pastes it into the Streamlit app which makes a web request back to the Flask App to exchange the code for API access tokens (the Flask App maintains a DB of login codes and API access tokens for each user). Whilst this works, itā€™s not seamlessā€¦ hence all the jiggery pokery above.

Hope that makes sense?

P.S. Happy to have short web conf with you to demo my code exchange authentication solution.

Ah, I see! Yeah, thatā€™s a pain.

Rather than solving it this way - which will continue to be painful, as it doesnā€™t mesh well with the Streamlit execution model - Iā€™d suggest trying to move the auth step back up to the client (possibly with a proxy in between, if the Auth0 endpoint requires a non-null origin).

Components do allow API calls, but the allow-same-origin restriction definitely makes things trickier, because you need to make sure that CORS is properly configured at every link in the chain that returns a response to your componentā€™s frontend.

The Flask example code I posted in the earlier question should work, for example. I tested it locally before posting it, just running with the simple flask dev server, via flask run --port 3001.

If youā€™re not able to get that example code to work, it might make more sense to begin with trying to debug that issue first. I wonder if the proxy you mentioned having set up is causing issues?

In my case, Streamlit is running on port 4008, the React component on port 3001, and the Flask API on port 8888. I have a proxy setting in Reactā€™s package.json redirecting all unknown routes to 8888.

Is your component built and running under Streamlitā€™s port? What if you set it up as I have?

My setup is identical to yours (Streamlit, component, Flask API all on different ports) - but Iā€™m not using a node proxy. I can put together a sample, if itā€™s useful.

1 Like

If itā€™s not too much trouble, that would be great. Thank you.

@tim I hope I can save you some time, because I have managed to make it work following your guidance. In fact, I got both the session state + rerun approach (with my delegated web request to Streamlit) and direct api call schemes working. The latter is by far preferable!! I had unfortunately got { mode: ā€œno-corsā€ } in my fetch header (in this post).

The rerun method is one time step behind the data that comes from the API call which is returned to the component to display, so wouldnā€™t be suitable. (I have a timestamp on it.)

I will post my solution as a gist ASAP. In the meantime, anyone reading this who wants it can send me a DM.

Many thanks for your assistance!

1 Like

Oh, great to hear! Good catch on the no-cors header - CORS is a massive pain, and I get it wrong literally every time I have to use it.

1 Like

Hi @tim,
Iā€™ve put my app up on GitHub for the community. Hereā€™s a brief post about it with a demo gif.

1 Like