Do A Real Deploy
- Deploy the latest main on shard.homework.quest.
- Try to log in.
Form -> LiveView Demo
(demo was started with)
mix phx.new lvdemo --database sqlite3
cd lvdemo
mix ecto.setup
Let’s look at the demo app from the scratch repo.
Part 1: A Form
In router:
- Path “/form” has a route for both get and post.
- We submit a form, the server can re-render the page.
Part 2: A GenServer
- CommentServer
- Note that it needs to get started from Application
- Demo it with iex -S mix
Part 3: A Channel + JS
- chan.html.heex
- comment_channel.js (user socket, app.js, etc)
Part 4: A LiveView
- Template, similar to a “dead view”.
- Associated elixir module with callbacks.
- There’s an implicit channel (which is a GenServer) here.
- Page on client is kept in sync with state on server.
 
- We’re using a .form / .input setup, which would be typical for a deadview too (e.g. it handles _csrf_token).
title: “cs4140 Notes: 19 React” date: “2024-10-04” #
Let’s set up React and a React component for PartyAnimal
cd assets
corepack enable pnpm
pnpm add react
pnpm add flowbite-react
- https://flowbite.com/docs/getting-started/react/
- https://flowbite-react.com/docs/components
- Alt: https://sparkui.vercel.app/?path=/docs/getting-started--docs
Update tailwind config to look in jsx files:
module.exports = {
  content: [
    "./js/**/*.js",
    "./js/**/*.jsx",  // Here
In app.js:
import "./invites/main";
In invites/main.jsx:
import React from 'react';
import { createRoot } from 'react-dom/client';
import Invites from './invites';
const root_div = document.getElementById('invites-main');
if (root_div) {
  const root = createRoot(root_div);
  root.render(<Invites />);
}
In invites/invites.jsx:
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Card } from 'flowbite-react';
export default function Invites(props) {
  return (
    <div className="flex items-center justify-center">
      <Card className="max-w-sm">
        <p>Invites go here</p>
      </Card>
    </div>
  );
}
In …/page_html/home.html.heex:
  <div id="invites-main">
    React component loading...
  </div>
In invites/api.js:
const base = "/ajax/invites";
export async function list_invites() {
  let resp = await fetch(base, {
    headers: {
      'Accept': 'application/json',
    }
  });
  return await resp.json();
}
And now update invites.jsx:
import { list_invites } from './api';
export default function Invites(props) {
  const [invites, setInvites] = useState([]);
  useEffect(() => {
    list_invites().then((xs) => setInvites(xs));
  }, []);
  console.log(invites);
  let invite_items = invites.map((item, ii) => (
    <li key={ii}>
      { item.name }
    </li>
  ));
  return (
    <div className="flex items-center justify-center">
      <Card className="max-w-sm">
        <ul>
          { invite_items }
        </ul>
      </Card>
    </div>
  );
}
Digression: Phoenix UI #
We don’t need to push to React just to get already styled UI components:
