Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs

name: Test and Lint

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
node-version:
- 20.x
- 22.x
- 24.x
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run build
- run: npm test
- run: npm run lint
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.md
5 changes: 5 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"printWidth": 100,
"tabWidth": 2,
"singleQuote": true
}
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,69 @@
# derby-react
# @derbyjs/derby-react

`@derbyjs/derby-react` enables React components to be integrated with DerbyJS apps and/or Racer models:

- React component state that's two-way synced with Racer model data
- Derby helper to render a React component inside a Derby component, with automatic cleanup

React version requirements:

- `useRacerState` supports React versions 16 and higher.
- The Derby `renderReactComponent` helper requires React 18 and higher due to using `reactDom.createRoot`, which was introduced in React 18.

## Example

This example uses a Derby component with a directory containing index.html and index.tsx.

```html
<!-- index.html -->
<index:>
<h2>Derby controls</h2>
<div>
<div>Message: <input value="{{message}}"></div>
<div>Counter: <span>{{counter}}</span> <button on-click="model.increment('counter', 1)">Increment</button></div>
</div>
<h2>React controls</h2>
<div as="reactAppContainer"></div>
```

```tsx
// index.tsx
import { Component } from 'derby';
import { renderReactComponent, useRacerState } from '@derbyjs/derby-react';
import type { Model } from 'racer';

class ReactDerbyExample extends Component {
static view = {
file: __dirname,
is: 'derby-react-example',
};

declare reactContainer: HTMLElement;

create() {
renderReactComponent(this, this.reactContainer, <ReactControls model={this.model} />);
}
}

function ReactControls({ model }: { model: Model }) {
// useRacerState accepts a Racer model scoped to the desired value's path
const $message = model.at<string>('message');
const [message, setMessage] = useRacerState($message);
const $counter = model.at<number>('counter');
const [counter] = useRacerState($counter, 0);

return (
<>
{/* State can be updated with a React state setter... */}
<div>
Message: <input value={message} onChange={(e) => setMessage(e.target.value)} />
</div>
{/* ...or state can also be updated with model methods. */}
<div>
Counter: <span>{counter}</span>{' '}
<button onClick={() => $counter.increment()}>Increment</button>
</div>
</>
);
}
```
24 changes: 24 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// @ts-check

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import eslintConfigPrettier from 'eslint-config-prettier/flat';

export default tseslint.config(
eslint.configs.recommended,
tseslint.configs.strict,
eslintConfigPrettier,
{
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
},
},
);
Loading