We're Participating in #Hacktoberfest2024! 🤩 Check out our open issues and help us simplify multiplayer game development!
Docs
Overview
Get Started

Getting Started

A small React Counter example to get you up and running with Movex.

You can see the finished code here https://github.com/movesthatmatter/movex-counter-example (opens in a new tab).

Pre-requisites: Create React App and Run it

See more here (opens in a new tab).

npx create-react-app my-movex-app --template typescript
cd my-movex-app
yarn start

1. Install it

yarn add movex movex-react; yarn add --dev movex-service

2. Create the Movex Config File

src/movex.config.ts
export default {
  resources: {},
};

3. Start Movex in Dev Mode

npx movex dev

4. Wrap the App in the MovexProvider

src/App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { MovexProvider } from 'movex-react';
import movexConfig from './movex.config';
 
export default function App() {
  return (
    <MovexProvider
      movexDefinition={movexConfig}
      socketUrl="localhost:3333"
      onConnected={() => {
        console.log('Movex Connected');
      }}
    >
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.tsx</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
    </MovexProvider>
  );
}

5. Add a Movex Resource

In this short tutorial we're going to build a Counter App that will sync in real-time with another client out of the box.

Add the movex file (action, state, reducer) to define the resource.
src/counter.movex.ts
import { Action } from 'movex';
 
// State
export type CounterState = {
  count: number;
};
 
export const initialCounterState: CounterState = {
  count: 0,
};
 
// Actions
export type CounterActions =
  | Action<'increment'>
  | Action<'decrement'>
  | Action<'incrementBy', number>;
 
// Reducer
export default reducer = (
  state = initialCounterState,
  action: CounterActions
) => {
  if (action.type === 'increment') {
    return {
      ...state,
      count: state.count + 1,
    };
  }
 
  if (action.type === 'decrement') {
    return {
      ...state,
      count: state.count - 1,
    };
  }
 
  if (action.type === 'incrementBy') {
    return {
      ...state,
      count: state.count + action.payload,
    };
  }
 
  return state;
};
And Reference it in the movex.config file by a name (counter)
src/movex.config.ts
import counterReducer from './counter.movex';
 
export default {
  resources: {
    counter: counterReducer,
  },
};

6. Let's add the Counter Component

For this tutorial, we'll keep the UI very basic since it's not the main point.

src/Counter.tsx
import { useMovexBoundResourceFromRid } from 'movex-react';
import React from 'react';
import movexConfig from './movex.config';
import { ResourceIdentifier } from 'movex';
 
type Props = {
  rid: ResourceIdentifier<'counter'>;
};
 
export const Counter: React.FC<Props> = (props) => {
  const counter = useMovexBoundResourceFromRid(movexConfig, props.rid);
 
  if (!counter) {
    return null;
  }
 
  return (
    <div>
      <button>Increment</button>
      <h1>{counter.state.count}</h1>
      <button>Decrement</button>
    </div>
  );
};
What's happening here?

On line 11 we're binding to the counter resource by rid. This means that now we get access to the Resource state by accessing counter.state and to the Action Dispatcher by accessing counter.dispatch()

In the following lines we're rendering a very simple UI, which displays the Counter State at line 20 and adding two Buttons which will be responsible for triggering the "Increment" and "Decrement" actions.

7. And hook it up with our App

src/App.tsx
import React, { useMemo } from 'react';
import './App.css';
import { MovexProvider } from 'movex-react';
import movexConfig from './movex.config';
import { toRidAsStr, ResourceIdentifier } from 'movex';
import { initialCounterState } from './counter.movex';
import { Counter } from './Counter';
 
function App() {
  const counterRid = useMemo(() => {
    const params = new URL(window.location as any).searchParams;
    return params.get('rid') as ResourceIdentifier<'counter'>;
  }, [window.location.href]);
 
  return (
    <MovexProvider
      movexDefinition={movexConfig}
      socketUrl="localhost:3333"
      onConnected={(instance) => {
        console.log('Movex Connected');
 
        if (!counterRid) {
          instance
            .register('counter')
            .create(initialCounterState)
            .map((counterResource) => {
              window.location.href += `?rid=${toRidAsStr(counterResource.rid)}`;
            });
        }
      }}
    >
      <div
        className="App"
        style={{
          height: '100vh',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        {counterRid && <Counter rid={toRidAsStr(counterRid)} />}
      </div>
    </MovexProvider>
  );
}
 
export default App;

What's going on here?

To keep things simple, we're creating a new Counter Resource as soon as posisble – once a movex instance is connected (Lines 22-29).

Once the Resource gets created (line 27), we add its rid (ResourceIdentifier) to the url param so it can be used by an outsider as well. More on this later.

Then, render the Counter Component if there is a rid set in the url params.

This is how it looks like so far:


Hello

8. Let's add the Actions

At this point we can see the state of the Counter Resource but cannot do much with it.

Let's make that possible by dispatching some actions in the on click handlers of the "Increment" and "Decrement" buttons we created above.

It should look like this.

src/Counter.tsx
import { useMovexBoundResourceFromRid } from 'movex-react';
import React from 'react';
import movexConfig from './movex.config';
import { ResourceIdentifier } from 'movex';
 
type Props = {
  rid: ResourceIdentifier<'counter'>;
};
 
export const Counter: React.FC<Props> = (props) => {
  const counter = useMovexBoundResourceFromRid(movexConfig, props.rid);
 
  if (!counter) {
    return null;
  }
 
  return (
    <div>
      <button
        onClick={() => {
          counter.dispatch({
            type: 'increment',
          });
        }}
      >
        Increment
      </button>
      <h1>{counter.state.count}</h1>
      <button
        onClick={() => {
          counter.dispatch({
            type: 'decrement',
          });
        }}
      >
        Decrement
      </button>
    </div>
  );
};

And you should be able to see this:


Hello

9. Now bring in multi-players

Open the URL in another browser tab or 2, or even 9 and start clicking the buttons. The changes will be synced in realtime, without any server-side coding.

Hello

The world is your oyster now – go make multiplayer gameees! :D

10. Homework

If you want an extra step, implement the "incrementBy" Action and functionality.