Using Aurelia Store with React

For whatever reason, I thought today lets give it a try and see whether I can make the Aurelia Store plugin work with a React app and turns out it works quite well (https://github.com/zewa666/react-aurelia-store):

  • npx create-react-app my-app --typescript
  • npm install aurelia-store aurelia-pal aurelia-pal-browser
  • Initialize pal in App.tsx
import React from 'react';
import { initialize } from "aurelia-pal-browser";
import './App.css';
import { FrameworkList } from './components/FrameworkList';
import { NewFramework } from './components/NewFramework';

initialize();

const App: React.FC = () => {
  return (
    <div className="App">
      <header className="App-header">
        <NewFramework></NewFramework>
        <FrameworkList></FrameworkList>
      </header>
    </div>
  );
}

export default App;

And here are my two components.

// FrameworkList.tsx
import React from 'react';
import { useStore } from "../store/state";

export const FrameworkList: React.FC = () => {
  const [state] = useStore();

  return <ul>
    {
      ((state && state.frameworks) || []).map((framework, key) => [
        <li key={key}>{ framework }</li>
      ])
    }
  </ul>;
}

// NewFramework.tsx
import React, { useState } from 'react';
import { useStore } from "../store/state";
import { addFramework } from '../store/actions';

export const NewFramework: React.FC = () => {
  const [, store] = useStore();
  const [framework, setFramework] = useState("");

  const addNewFramework = () => {
    store.dispatch(addFramework, framework);
    setFramework("");
  }

  return <div>
    <label>New framework:</label>
    <br />
    <input type="text"
      placeholder="enter the new framework"
      defaultValue={framework}
      onBlur={(e) => setFramework(e.target.value)} />
    <button onClick={addNewFramework}>Add</button>
  </div>;
}

Finally here’s the contents of state.ts

import { Store } from "aurelia-store";
import { useEffect, useState } from "react";

export type State = {
  frameworks: string[];
}

export const initialState = {
  frameworks: ["Aurelia", "React", "Angular"]
} as State;


export const store = new Store<State>(initialState, {});

export const useStore = (): [State, Store<State>] => {
  const [state, setState] = useState(initialState);

  useEffect(() => {
    const subscription = store.state.subscribe((state) =>setState(state));

    return () => subscription.unsubscribe();
  });

  return [
    state,
    store
  ];
}

It’s essentially creating a new custom hook called useStore, which will create a subscription (plus handling unsubscribes). In the subscription it will set the consuming components state with the newly streamed state. Whenever a new state comes along it updates the components internal state, thus triggers a re-render.

6 Likes

Great!

On a slightly related note, I’m using a very nice debugging tool called logrocket (https://logrocket.com/) and it plugs in directly into Redux store.

It has been on my backlog for a while to try to make it works with aurelia-store, but I haven’t found the time to give that a try. If anyone willing to take that on, I believe it will be a huge benefit to any app that uses aurelia-store.

1 Like

Updated the repo to use immerjs and an overload for useStore to pass a custom selector, similar to what the connectTo decorator does.

3 Likes