Did anyone integrate React18 with LiveView? I know this is an Elixir/Phoenix forum, and I am not sure this is an Elixir problem, but I imagine that the mobile market - thus React Native - is so big that I may find some experience here. So, with the new React 18 api, I have multiple renderings, and it’s not the StrictMode issue. I am curious because I have to return the root from the component to make it work. The Liveview docs are pretty dry on this. I used mounted
, push_event
and pushEventTo
.
In any case, I can share the demo code: I tried two different approaches:
- an event sends data to the server to mutate it and is sent back to the client for rendering,
- an event mutates the data client side and the data is sent to the server to display outside of the component for example.
Both give multiple renderings (the logs)
export function mountCounters(id, opts) {
console.log('mount');
const container = document.getElementById(id);
const root = createRoot(container);
root.render(
<React.StrictMode>
<Counters {...opts} />
</React.StrictMode>
);
return root; // <- ?
}
export const CounterHook = {
CounterA: {
mounted() {
const root = mountCounters('a', this.props());
this.handleEvent('updateCount', ({ newCount: newCount, inc: inc }) => {
const props = this.props(newCount, inc);
root.render(<Counters {...props} />);
^^^
});
},
props(counter = 0, inc = 5) {
return {
inc: inc,
count: counter,
sendMsg: () =>
this.pushEventTo('a', 'incCount', {
counter: counter,
inc: inc,
}),
pushMsg: c => this.pushEventTo('a', 'incremented', { counter: c }),
};
},
},
};
export const Counters = ({ inc, sendMsg, pushMsg, count = 0 }) => {
const [counter, setCounter] = React.useState(0);
const incCounter = () => {
setCounter(counter => counter + inc);
pushMsg(counter + inc);
};
console.log(counter, count);
return (
<section className='phx-hero'>
<h1>SSR of the value of the counter</h1>
<button onClick={sendMsg}>+{inc}</button>
<br />
<p>{count}</p>
<br />
<hr />
<h1>React rendered counter in component</h1>
<button onClick={incCounter}>+{inc}</button>
<br />
<span>{counter}</span>
</section>
);
};
def mount(_params, _session, socket) do
socket = socket |> assign(count: 0, value: 0)
{:ok, socket}
end
def handle_event("incCount", %{"counter" => counter, "inc" => inc}, socket) do
IO.puts("push")
new_count = counter + inc
socket = assign(socket, count: new_count, inc: inc)
{:noreply, push_event(socket, "updateCount", %{newCount: new_count, inc: inc})}
end
def handle_event("incremented", %{"counter" => counter}, socket) do
IO.puts("receive")
IO.inspect(counter, label: "value")
socket = assign(socket, :value, counter)
{:noreply, socket}
end
def render(assigns) do
IO.puts("render")
~H"""
<div id="a" phx-hook="CounterA" phx-update="ignore"></div>
<h1>Pushed by React from the component: <%= @value %></h1>
"""
end