Bootstrapping React
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
import { ThemeProvider } from './contexts/ThemeContext.tsx';
// Just for demo that context can reach even on children, grandchildren etc
function Main() {
return <App></App>;
}
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<ThemeProvider>
<Main />
</ThemeProvider>
</React.StrictMode>
);
Bootstrapping Angular
<!DOCTYPE html>
<html lang="en">
<head>
<title>My app</title>
<meta charset="UTF-8" />
</head>
<body>
<main-root>Loading...</main-root>
</body>
</html>
import 'zone.js';
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AsyncPipe, NgStyle } from '@angular/common';
import { App } from './app/app';
@Component({
selector: 'main-root',
standalone: true,
imports: [AsyncPipe, NgStyle, App],
template: `<app-root></app-root>`,
})
// Just for demo that service can reach even on children, grandchildren etc
export class Main {
name = 'Angular';
}
bootstrapApplication(Main);
React's App component
import './App.css';
import { useTheme } from './contexts/ThemeContext';
export default function App() {
// Destructuring make names lose context
// const { theme, toggleTheme } = useTheme();
// I feel explicit is better, so things won't appear as local
const themeContext = useTheme();
const name = 'App';
return (
<div
style={{
background: themeContext.theme === 'dark' ? '#222' : '#eee',
color: themeContext.theme === 'dark' ? '#eee' : '#222',
padding: '2rem',
}}
>
<h1>Hello from {name}!</h1>
<h3 onClick={themeContext.toggleTheme}>
{themeContext.theme.toUpperCase()} MODE
</h3>
<a target="_blank" href="https://react.dev/">
Learn more about React
</a>
</div>
);
}
Angular's app-root component
import { Component } from '@angular/core';
import { ThemeContext } from './contexts/theme.context';
import { AsyncPipe, NgStyle } from '@angular/common';
@Component({
selector: 'app-root',
standalone: true,
templateUrl: './app.html',
imports: [NgStyle, AsyncPipe],
})
export class App {
name = 'App';
// This works too, just seems complicated, and also it looks like local properties
// theme$: Observable<Theme>;
// toggleTheme: () => void;
// This works too, but it looks like local properties
// get theme$() { return this.themeContext.theme$; }
// toggleTheme() { this.themeContext.toggleTheme(); }
constructor(public themeContext: ThemeContext) {
// This works too, just seems complicated, and also it looks like local properties
// ({theme$: this.theme$, toggleTheme: this.toggleTheme } = themeContext);
// this.toggleTheme = this.toggleTheme.bind(themeContext);
//
// This works too, just seems complicated, and also it looks like local properties
// this.theme$ = this.themeContext.theme$;
// this.toggleTheme = this.themeContext.toggleTheme.bind(themeContext);
}
}
<div
[ngStyle]="{
background: (themeContext.theme$ | async) === 'dark' ? '#222' : '#eee',
color: (themeContext.theme$ | async) === 'dark' ? '#eee' : '#222',
padding: '2rem'
}"
>
<h1>Hello from {{ name }}!</h1>
<h3 (click)="themeContext.toggleTheme()">
{{(themeContext.theme$ | async)?.toUpperCase()}} MODE
</h3>
<a target="_blank" href="https://angular.dev/overview">
Learn more about Angular
</a>
</div>
React Context:
import { createContext, ReactNode, useContext, useState } from 'react';
type Theme = 'light' | 'dark';
interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>('light');
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
function toggleTheme() {
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
}
}
export function useTheme(): ThemeContextType {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
Angular @Injectable
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
export type Theme = 'light' | 'dark';
@Injectable({
providedIn: 'root',
})
export class ThemeContext {
private themeSubject = new BehaviorSubject<Theme>('light');
theme$ = this.themeSubject.asObservable();
toggleTheme() {
const newTheme = this.themeSubject.value === 'light' ? 'dark' : 'light';
this.themeSubject.next(newTheme);
}
}
React Context ◆
Angular @Injectable