Skip to main content

React

Getting started

Let's create a little app to demonstrate how to use React in flecks:

npm init @flecks/app react-test
cd react-test

We'll also create a little fleck to hold our root component:

npm init @flecks/fleck -w packages/root

We'll add the react fleck to our new fleck:

cd packages/root
npx flecks add @flecks/react
tip

We add @flecks/react to packages/root instead of our application directory so that our new fleck encapsulates all the dependencies it needs. You can distribute your flecks to others if you do this right!

Add a root component

Let's implement a hook to add a component to @flecks/react's root components:

packages/root/src/index.js
import Component from './component';

export const hooks = {
'@flecks/react.roots': () => Component,
};

Let's also add our component source file:

packages/root/src/component.jsx
import {React} from '@flecks/react';

function Component() {
return <p>hello world (from React)</p>;
}

export default Component;
Hey, where's my React?

You may notice we imported React from @flecks/react instead of react. This is provided as a convenience. flecks is a very dynamic system and it may also be possible to load multiple React versions on your page.

Using @flecks/react makes sure your components are all using the same instance of React.

Go check it out

Start your application:

npm run start

Visit your website in your browser and you will see the hello world message!

Server-Side Rendering (SSR)

If you disable JavaScript in your browser and reload the page you will still see the message. This is because Server-Side Rendering (SSR) is enabled by default! If you don't want this, update your build/flecks.yml:

build/flecks.yml
'@flecks/core':
id: react-test
'@flecks/react':
ssr: false
'@flecks/server': {}
'@react-test/root:./packages/root/src': {}

Now if you visit the page with JavaScript disabled, you will get a white page.

Hot module reloading

You'll notice that if you edit your component, the changes are immediately reflected on the page. This is because we have HMR support in our application!

Routing

flecks provides @flecks/react/router which implements server and client routing using React Router's <RouterProvider>.

Just add it to flecks.yml:

build/flecks.yml
'@flecks/core':
id: react-test
'@flecks/react': {}
'@flecks/react/router': {}
'@flecks/server': {}
'@react-test/root:./packages/root/src': {}

Filetree routing

It is possible to define your routes as a file structure. A helper is provided to build a routes object from your file structure:

import {createRoutesFromContext} from '@flecks/react/router';

export const hooks = {
'@flecks/react/router.routes': () => (
createRoutesFromContext(require.context('./routes'))
),
};

Supposing your ./routes directory was structured as:

routes
├─ index.jsx
├─ [...catchall].jsx
├─ -optional.jsx
├─ -[optionalDynamic].jsx
├─ about.jsx
└─ team/
└─ [teamId]/
├─ index.jsx
└─ edit.jsx

Your routes object would look like:

[
{path: '/', ...},
{path: '*', ...},
{path: '/about', ...},
{path: '/optional?', ...},
{path: '/:optionalDynamic?', ...},
{
path: '/team', ...,
children: [
{
path: ':teamId', ...
},
{
path: ':teamId/edit', ...
},
],
},
]

Each route object has a ... in the diagram above because each module is import()ed and the exports are mixed in to the Route. Route modules should probably at least export function Component() { ... }, but may export any of the Route props.

You may export a default export which will be used as the component. An explicit named Component export will take precedence.

Selecting the root

There can only be one root of your router tree. Supposing you implemented @flecks/react/router.routes in your fleck called @my/routes-root, you would set the following configuration in build/flecks.yml to use your fleck's routes as the root:

'@flecks/react/router':
root: '@my/routes-root'

Hooks

useFlecks()

You may use this hook from your components to gain access to the flecks instance.

Example:

function Component() {
const flecks = useFlecks();
const id = flecks.get('@flecks/core.id');
return <p>Your application ID is {id}</p>;
}

useEvent(object, eventName, fn)

object

The event emitter to listen to

eventName

The name of the event to listen for.

fn

The event handler to call.

Example:

function Component() {
const flecks = useFlecks();
const [isConnected, setIsConnected] = useState(false);
useEvent(flecks.socket, 'connect', () => {
setIsConnected(true);
});
useEvent(flecks.socket, 'disconnect', () => {
setIsConnected(false);
});
return <p>Socket is {isConnected ? 'connected' : 'disconnected'}.</p>;
}

usePrevious(value)

value

The value whose previous value we're interested in.

See: https://blog.logrocket.com/accessing-previous-props-state-react-hooks/