gradio icon indicating copy to clipboard operation
gradio copied to clipboard

`gr.State` cannot be passed to JavaScript function

Open space-nuko opened this issue 2 years ago • 5 comments

Describe the bug

I want to run an event handler that does some processing and returns a string, then passes that output string to a JavaScript function

So I figured I'd store the string in a gr.State and put it as the input to the JS function, however, the JS function only gets null instead. If I pass a Python callback that does see the resulting state though

Is there an existing issue for this?

  • [X] I have searched the existing issues

Reproduction

Place some text in the textbox and click each button in sequence

#!/usr/bin/env python

import gradio as gr


with gr.Blocks() as demo:
    textbox1 = gr.Textbox()
    btn = gr.Button("Go", variant="stop")
    btn2 = gr.Button("Go2", variant="stop")
    btn3 = gr.Button("Go3", variant="stop")

    def ppr(st):
        print("GET: " + str(st))

    js = "(x) => { console.log(x); return x; }"
    css_state = gr.State("")
    btn.click(fn=lambda x: x, inputs=[textbox1], outputs=[css_state]).then(fn=ppr, _js=js, inputs=[css_state], outputs=None)
    btn2.click(fn=ppr, _js=js, inputs=[css_state], outputs=None)
    btn3.click(fn=ppr, _js=js, inputs=[textbox1], outputs=None)


demo.queue().launch(server_port=9876)

Screenshot

No response

Logs

Python stdout:

GET: asdf!
GET: asdf!
GET: asdf!

JS console:

null
null
asdf!

System Info

3.22.1, Windows, Chromium

Severity

annoying

space-nuko avatar Mar 19 '23 23:03 space-nuko

So state is only stored in the backend only, this is so that state can support binary, non-serializable data. You can maintain frontend state by storing a global variable in window, e.g. window.state_data = {}. Will think about if we should send non-binary state data to the frontend, and how to better document this all

aliabid94 avatar Mar 20 '23 05:03 aliabid94

I waste 1 hour to find out a problem is caused by this issue.

If _js can not work with gr.State, and you don't treat it as a bug, don't have plan to fix it, then write it in document.

butaixianran avatar Apr 05 '23 17:04 butaixianran

So state is only stored in the backend only, this is so that state can support binary, non-serializable data. You can maintain frontend state by storing a global variable in window, e.g. window.state_data = {}. Will think about if we should send non-binary state data to the frontend, and how to better document this all

if state is stored in the backend, when will they be deleted from memory ? When the web page is closed ? Are there any risk of memory leak when there are many users?

binary-husky avatar Dec 07 '23 13:12 binary-husky

@aliabid94 I tried the state solution for gradio_folium and it still doesn't work:

import gradio as gr
from gradio_folium import Folium
from folium import Map, Element, LatLngPopup
import pandas as pd
import pathlib

def click(a):
    print('hellp')
    print(a)

def inject_javascript(map):
    js = """
    document.addEventListener('DOMContentLoaded', function() {
        map_name_1.on('click', function(e) {
            window.state_data = e.latlng
        });       
    });
    """
    map.get_root().html.add_child(Element(f'<script>{js}</script>'))


with gr.Blocks() as demo:
    map = Map(location=[25.7617, 80.1918])
    map._name = "map_name"
    map._id = "1"

    LatLngPopup().add_to(map)
    inject_javascript(map)

    fol = Folium(value=map, height=400)
    js =  "(a) => {console.log(window.state_data); return window.state_data;}" # -> window is null
    button = gr.Button("Get results")
    button.click(click, inputs=[fol], js=js)

demo.launch()

it's probably a scope problem but still there should be a way to bypass it.

ysig avatar Apr 16 '24 23:04 ysig

if state is stored in the backend, when will they be deleted from memory ? When the web page is closed ? Are there any risk of memory leak when there are many users?

State is deleted 60 minutes after a user closes the page (to allow for reconnections in case a connection is dropped momentarily)

freddyaboulton avatar Apr 22 '24 15:04 freddyaboulton

There is a solution, listen to the change event of gr.State, output to a gr.JSON, then listen to the change event of gr.JSON, js can get the value

xinyii avatar Sep 13 '24 10:09 xinyii

Thank you for the workaround @xinyii !

Will close as we designed gr.State to be stored only in the server.

freddyaboulton avatar Sep 13 '24 14:09 freddyaboulton