2

I wrote a React-App and now I want to implement some Tab-Navigation.

The problem is, my react app is integrated in a Symfony-Project that uses Twig templates. So my React-App lives in templates/dashboard/index.html.twig:

{% block content %}
    <div id="react-app"></div>
{% endblock %}

And the tab-menu lives in templates/navigation/navbar.html.twig :

<div class="flex-1 px-4 flex justify-between">
    <div id="react-tabs"></div>
</div>

react-App.js renders both of the apps.

import React from 'react';
import {render} from 'react-dom';
import {ready} from '../../helper/vanilla-helper'
import ReactTabs from "./ReactTabs";

function ReactApp() {
    return (
        <div className="App p-20 bg-blue-300">
            Blubber
        </div>
    );
}

export default ReactApp;

ready(() => {

    render(
        <ReactApp/>,
        document.getElementById('react-app')
    );

    render(
        <ReactTabs/>,
        document.getElementById('react-tabs')
    );
});

I did quite a lot of research in the internet about sharing state. But it seems all of this is only related to sharing state inside of ONE ReactJs App and between their components.

In my case I need to share state between two apps. Is this even possible or should I take a complete different approach to this?

Slowwie
  • 1,146
  • 2
  • 20
  • 36
  • If you want to share state between two different applications you can use Sockets.io which uses websocket. With sockets.io you can make your two applications to call a method which sends the updated state to the other application. Sockets.io is biderectional. Thus, everytime you call setstate you can also send the updated state in the callback of your setState. Do you want me to create a simple example with such approach? – Maf Apr 23 '20 at 12:14
  • I create some kind of reuseable web-cms-framework. I don't want my bundle to depend on a websocket server configuration. I mean I just need some state like: aktiveTab = 2; – Slowwie Apr 23 '20 at 13:15

1 Answers1

0

You can make a simple store (as in state management) to share the state between components. If you need a more serious solution, I suggest you should look into Redux / MobX or some other state management tool.

In the snippet only a very basic counter is shared - it's easier to follow through like this:

const { useState, useEffect, useMemo } = React

// observer pattern
const makeObservable = (target) => {
    let listeners = []
  let value = target
  
  function get() {
    return value
  }
  
  function set(newValue) {
    if (value === newValue) return
    value = newValue
    listeners.forEach((l) => l(value))
  }
  
  function subscribe(listenerFunc) {
    listeners.push(listenerFunc)
    return () => unsubscribe(listenerFunc) // can be used in the useEffect
  }

  function unsubscribe(listenerFunc) {
    listeners = listeners.filter((l) => l !== listenerFunc)
  }

  return {
    get,
    set,
    subscribe,
  }
}

const simpleStore = makeObservable({ count: 0 })

const useStore = () => {
  const [counter, setCounter] = useState(simpleStore.get())

  useEffect(() => {
    return simpleStore.subscribe(setCounter)
  }, [])

  const actions = useMemo(() => {
    return {
      incrementCount: () => simpleStore.set({ ...counter, count: counter.count + 1 }),
      decrementCount: () => simpleStore.set({ ...counter, count: counter.count - 1 }),
    }
  }, [counter])

  return {
    state: counter,
    actions
  }
}

const App = () => {
    const { state, actions } = useStore()

  return (
    <div>
      {state.count}
      <button
        onClick={() => actions.incrementCount()}
      >
        INCREMENT
      </button>
      <button
        onClick={() => actions.decrementCount()}
      >
        DECREMENT
      </button>
    </div>
  )
}

const Tabs = () => {
    const { state, actions } = useStore()

  return (
    <div>
      {state.count}
      <button
        onClick={() => actions.incrementCount()}
      >
        INCREMENT
      </button>
      <button
        onClick={() => actions.decrementCount()}
      >
        DECREMENT
      </button>
    </div>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('app')
);

ReactDOM.render(
  <Tabs />,
  document.getElementById('tabs')
);
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>

<div id="app"></div>
<div id="tabs"></div>

Thanks streletss for the great example on the makeObservable & the custom hook!

muka.gergely
  • 8,063
  • 2
  • 17
  • 34