fidr / phoenix_live_react

Render React.js components in Phoenix LiveView views
MIT License
238 stars 42 forks source link

Surgically inject prop changes only through web socket #4

Closed bliitzkrieg closed 3 years ago

bliitzkrieg commented 4 years ago

Currently when a socket variable changes on the server, the entire HTML of the component returns in the web socket connection. Is there anyway to only patch the props change through the web socket to keep the payload size down? I have a lot of props with lots of data and the socket message is huge.

Thanks!

fidr commented 4 years ago

Hi, thanks for the feedback! I like the idea but it might get a bit too complex. Also it might be something that liveview ends up optimising itself or catering for in hooks themselves.

I will take a look to see if I can optimise the payload. Currently it's using content_tag and that might also be a reason for unneeded payload. Does it send the full payload on any socket variable change, or only when it's related to the component?

bliitzkrieg commented 4 years ago

Only when it's related to the component

fidr commented 4 years ago

I've pushed some changes to master that might help here. If you'd like to you can try them out with. I will release it after some more testing:

{:phoenix_live_react, github: "fidr/phoenix_live_react"}

Merge props

If you set the merge_props: true option on your component, newly received props will be merged with the previous props. So you can send %{my_field: "updated_value"} as props on an update, to skip sending the full list of props.

I think you'd have to assign the props to the socket assigns to make this work correctly (LiveView will then handle checking if props are changed).

<%= live_react_component "Components.MyComponent", @my_props_1, id: "my-component-1", merge_props: true %>

Alternative

LiveView now has a handleEvent function, you can use that to send updates from your LiveView to the client side (based on a unique event name)

On the LiveView:

{:noreply, push_event(socket, "my-component-1-update", %{foo: "bar"})

In your react component:

  // Get handleEvent from props

  useEffect(() => {
    if (!handleEvent) return;
    handleEvent("my-component-1-update", (data) => {
      // use data to update your component
      console.log(data)
    })
  }, [handleEvent])