Chapter 15

Astroで、React CSR的な例

knaka Tech-Blog
knaka Tech-Blog
2023.04.15に更新

概要:

Astro2 で、フロントでReactを使えるか試す編です。

  • ベース部分は Astro SSR, ルーティング機能など

  • svelteKit 使うほうがシンプルですね。。

  • フロントJSで、ReactをCDNから読込んで使ってみました。


構成

  • Astro: 2
  • React 18

参考


参考のコード

https://github.com/kuc-arc-f/astro2_13react


実装の例

  • test/indes.astro
---
import Layout from '../../layouts/Layout.astro';

const PUBLIC_ANYBODY = import.meta.env.PUBLIC_ANYBODY;
const url = import.meta.env.PUBLIC_API_URL;
console.log("url=", url);

---
<!-- MarkUp -->
<Layout title="Welcome to Astro.">
	<div id="root" class="container">
    </div>
</Layout>
<!-- -->
<style>
</style>
<!-- -->
<script is:inline src="/js/Test.js"></script>
<script type="text/babel">
const useState = React.useState;
const useEffect = React.useEffect;
//Test.func1();
//
const App = () => {
    const [state, setState] = useState(0)
    const handleClick = () => setState(state + 1);

    return (
        <div>
            <h1>My React App with CDN</h1>
            <hr />
            カウント: {state}
            <hr />
            <button onClick={handleClick}>TestCount</button>
        </div>
    )
}
const container = document.getElementById("root");
const root = ReactDOM.createRoot(container);
root.render(<App />);
</script>


  • Layout: CDNで、React読込む例です。

  • script is:inline で、CDNファイル読みます。

---
export interface Props {
	title: string;
}

const { title } = Astro.props;
---

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<meta name="generator" content={Astro.generator} />
		<title>{title}</title>
		<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css"
		rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous" />    
		<!-- SQLITE -->
		<script is:inline src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.6.2/sql-wasm.min.js"></script>
		<!-- React -->
		<script is:inline src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
		<script is:inline src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
		<script is:inline src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
	</head>
	<body>
		<!-- Header -->
		<div class="text-center my-2">
			<a href={`/`}>[ Top ]</a>
			&nbsp;<a href={`/test`}>[ Test ]</a>
			&nbsp;<a href={`/gpt`}>[ GPT ]</a>
			<hr />
		</div>		
		<slot />
	</body>
</html>
<style is:global>
	:root {
		--accent: 124, 58, 237;
		--accent-gradient: linear-gradient(45deg, rgb(var(--accent)), #da62c4 30%, white 60%);
	}
	html {
		font-family: system-ui, sans-serif;
		background-color: #F6F6F6;
	}
	code {
		font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
			Bitstream Vera Sans Mono, Courier New, monospace;
	}
</style>
<script src="../lib/Layout.ts"></script>

<!--
&nbsp;<a href={`/login`}>[ Login ]</a>
 -->

  • 追加
  • useState, useEffect
  • component, prop
  • map 繰り返し
---
import Layout from '../../layouts/Layout.astro';

const PUBLIC_ANYBODY = import.meta.env.PUBLIC_ANYBODY;
const url = import.meta.env.PUBLIC_API_URL;
console.log("url=", url);

---
<!-- MarkUp -->
<Layout title="Welcome to Astro.">
	<div id="root" class="container">
    </div>
</Layout>
<!-- -->
<style>
</style>
<!-- -->
<script is:inline src="/js/Test.js"></script>
<script is:inline type="text/babel" src="/js/components/Child.js"></script>
<script type="text/babel">
const useState = React.useState;
const useEffect = React.useEffect;
//
const App = () => {
    const [state, setState] = useState(0)
    const [count, setCount] = useState(0);
    const [items, setItems] = useState([])
    const handleClick = () => setState(state + 1);
    const greeting = 'Welcome , React !!!';
    //
    useEffect(() => {
        const testItems =Test.getList();
        setItems(testItems);
        console.log(testItems);
    }, []);  
    //
    return (
        <div>
            <h1>My React Title</h1>
            <hr />
            カウント: {state}
            <hr />
            <button onClick={handleClick}>TestCount</button>
            <hr />
            <Child text={greeting} />
            <hr />
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
            <hr />
            {items.map((item, index) => {
                return (
                <div key={item.id}>ID: {item.id}, Name: {item.name}
                </div>
                )
            })}             
        </div>
    )
}
const container = document.getElementById("root");
const root = ReactDOM.createRoot(container);
root.render(<App />);
</script>


  • CRUD の一部

  • /src/pages/gpt2/index.astro

---
import Layout from '../../layouts/Layout.astro';
import CrudIndex from './CrudIndex';
const url = import.meta.env.PUBLIC_API_URL;
let serverConfig = {url: url};
serverConfig = JSON.stringify(serverConfig);
console.log(serverConfig);

---

<Layout title="Welcome to Astro.">
    <main class="container">
        <input type="hidden" id="server_config" value={serverConfig}>
        </input>
        <h1>Gpt2-React</h1>
        <a href={`/gpt2/create`} class="btn btn-primary">Create</a>
        <hr />
        <div id="root"></div>		
        <hr />
    </main>
</Layout>
<!-- -->
<style>
</style>
<!-- JS -->
<script is:inline src="/js/Test.js"></script>
<script is:inline src="/js/lib/todo/Crud.js"></script>
<script is:inline src="/js/lib/Common.js"></script>
<script is:inline type="text/babel" src="/js/components/Child.js"></script>
<script is:inline type="text/babel" src="/js/lib/todo/main.js"></script>
<!--
 -->


  • main.js : public/js/lib/todo/main.js
main.js
const prmServer = Common.getServerConfig();
console.log(prmServer);
const useState = React.useState;
const useEffect = React.useEffect;
//
const App = () => {
    const [items, setItems] = useState([])
    //
    useEffect(async() => {
        const testItems = await Crud.getList(prmServer);
        setItems(testItems);
        console.log(testItems);
    }, []);  
    //
    return (
        <div>
        {items.map((item, index) => {
            return (
            <div key={item.id}>
                <h3>{item.title}</h3>
                ID: {item.id}
                <span class="mx-2">
                    <a href={`/gpt2/show/${item.id}`} class="btn btn-sm btn-outline-primary"
                    >Show</a>					
                </span>
                <hr class="my-2" />
            </div>
            )
        })}             
        </div>
    )
}
const container = document.getElementById("root");
const root = ReactDOM.createRoot(container);
root.render(<App />);

....