Creates a Surface + LiveView container for rendering and updating React components.
- LiveView js hook which performs performs rendering or hydration of the component
- A LiveView React Context that is injected with each render/update providing access to LiveView js functions
- An optional SSR macro that can assist with generating placeholder or static react markup at compile time
- This runs a hydrate when mounting, as it was rendered server side
In the future:
- Integrate Surface with React Server Components if possible i.e use Surface for writing the React Server Component (Surface AST -> react/jsx?)
Create a components object to be passed into the hook generation function.
It is recommended to create a module that exports all your components used by react-surface.assets/js/components/index.js:
importComponent1from"./component1"importComponent2from"./component2"exportdefault{
Component1,
Component2,}assets/js/app.js:
importcomponentsfrom"./components"import{buildHook}from"react-surface";// pass component mapping to buildHook functionconstreactSurfaceHook=buildHook(components)// setup liveview as normal, merging your hooks with react-surface hooks.letliveSocket=newLiveSocket("/live",Socket,{{...otherhooks, ...reactSurfaceHook},params: {_csrf_token: csrfToken},});If not performing SSR via use ReactSurface.SSR this can be skipped.
mix gen_ssr_script
This is used to configure node_ssr at compile time to understand where your components are, and how many instances you are running
config:node_ssr,# REQUIRED - This be the folder with assets, and a package.json file - passed to erlexec `:cd` optionassets_path: "#{File.cwd!()}/assets",# this is the name of the script to be invoked - defaults to "ssr.js", can change it here.script_name: "ssr.js"Optional requirements:
component_path: "js/components"# this is the default, relative path to your components directory from assets/.component_ext: ".js"# this is the default, used with nodejs require statementscount: 1# this is the number of workers in the nodejs cluster - likely not necessary to have more than 1, unless rendering lots of componentsWhere props is a map of JSON serializable values.
<Reactcomponent="HelloReactSurface" props={{%{name: "Doug"}}}/>Supply an rid when rendering multiple of the same component on the same page. (translated to DOM id attrubute)
<Reactrid="unique_id" component="HelloReactSurface" props={{%{name: "Doug"}}}/>This will result in the following DOM being generated in Elixir.
<divid="SHA1:8"
rs-c="HelloReactSurface"
rs-p="eyJuYW1lIjogIkRvdWcifQo"
rs-m="r"
phx-hook="_RS"
><divid="r <> SHA1:8" phx-update="ignore"></div></div>The props are being base64 encoded (no padding) for the DOM attribute The ids are generated sha1 hashes, based on the component name, and optional rid prop
When server rendering it is similar - but with the rendered component contents as a child of the inner div on initial render, and a different hook
<divid="SHA1:8"
rs-c="HelloReactSurface"
rs-p="eyJuYW1lIjogIkRvdWcifQo"
rs-m="h"
phx-hook="_RS"
><divid="r <> SHA1:8" phx-update="ignore"><!-- SSRed REACT ROOT --></div></div>LiveView events can be accessed via the useLiveContext React hook exported from the javascript package.
This hook returns an object with the functions: {handleEvent, pushEvent, pushEventTo}
See the HelloReactSurface.js component for an example.
If available in Hex, the package can be installed
by adding react_surface to your list of dependencies in mix.exs:
defdepsdo[{:react_surface,"~> 0.1.0"}]endFrom github:
defdepsdo[{:react_surface,github: "harmon25/react_surface"}]endAdd react-surface as a dep in your package.json
{
"react-surface": "file:../deps/react_surface"
}In your assets dir:
npm install react react-dom --save
npm install @babel/preset-env @babel/preset-react --save-devAdd "@babel/preset-react" as a babel preset in .babelrc
Add the following to your assets/webpack.config.js file to ensure only a single react + react-dom is included in your bundle:
module.exports=(env,options)=>({// add:resolve: {alias: {react: path.resolve(__dirname,'./node_modules/react'),'react-dom': path.resolve(__dirname,'./node_modules/react-dom')}}// ...});This library is inspired by react-phoenix and phoenix_live_react.
Check em out if you want to use react components in an eex or leex templates.