Custom Component - Manual State Changes through st.session_state

As an exercise for myself, I’m creating a custom component: streamlit-gameboard

The idea is to have an abstract game board so that custom game logic can be implemented in Streamlit to create learning projects like Tic-Tac-Toe, Checkers, Chess, Connect Four, etc…including creating AI players.

I am completely new to React and TypeScript, so I am blindly stumbling through, working from the template provided by Streamlit. I have much documentation and cleanup to do.

The hiccup I have is in updating the component state from a manual manipulation of session state. I’ve got it working with one caveat: if the next action is outside of the component, the last manual change is reverted.

  1. Place a piece on the board with a click (renders fine)
  2. Manually remove the piece via an edit to session state (renders fine).

If step 3 is some other click within the component, all is well. However, if step 3 is a click outside of the component, that last manual change to session state will be forgotten/reverted.

gameboard

Within the gameboard render funciton, I have:

    const rows = this.props.args["rows"]
    const cols = this.props.args["cols"]
    const players: Array<any> = this.props.args["players"]
    const board_color = this.props.args["board_color"]
    const board_state_ss = this.props.args["board_state"]
    const key = this.props.args["key"]
    
    // Check if board_state is being passed back to accomodate manual modification
    if (board_state_ss !== null) {
      this.state.board_state = board_state_ss
    }

Note if anyone goes playing: Board size can’t be dynamically changed at the moment due to the partial implementation of manual state adjustments, but the colors and player names can be freely changed for an instantiated gameboard. And I have another bug to chase down where clicking away from the color picker to commit the change doesn’t work if I click within the custom component; I have to click anywhere outside of my custom component and outside of the color picker to dismiss the color picker window.

1 Like

I think I figured it out. I have to pass Streamlit.setComponentValue(this.state.board_state) within the render function whenever I override the state (appropriately protected from doing so unnecessarily so it doesn’t get stuck in a loop). I’ve got the board resizing working correctly again alongside manual state adjustments, so I just need to define a comparitor to check is the board state passed from Streamlit is different than the state in React.

I’ll update if the rest of that works out like I’m thinking.

Yay! Although I have no idea if I’m being terribly abusive of the system with this, I was able to pass manual session state manipulations back to the react component by including a couple extra steps at the beginning of its render function, after import all the arguments from this.props.args.

const change = (JSON.stringify(this.state.board_state) !== JSON.stringify(board_state_ss))

if (board_state_ss !== null) {
    this.state.board_state = board_state_ss
}

if (board_state_ss !== null && change){
    Streamlit.setComponentValue(this.state.board_state)
}

If anyone has any sagely advice that I should go about this differently, please let me know. But at least, yay, it’s working. :grin:

1 Like