Getting started

Getting Started with Eventrix

Eventrix is events and state management system which gives you tools to connect your UI with JS bussines logic. Component can send events to another components or JS bussines logic which give you posibility to send some XHR to get data from server. Eventrix also give you posibility to store some data in eventrix state. You can share this state with Your components and JS bussines logic part.

Events management

Eventrix class instance is something like store in Redux but not the same.

/eventrix/index.js
import { Eventrix } from 'eventrix';

const initialState = { users: [] };
const eventsReceiver = [];

const eventrix = new Eventrix(initialState, eventsReceiver);

Eventrix class receive initial state and events receivers to handle events and to do someting with event data. Eventrix instance has events managament system and state manager inside. When You emit some event by

eventrix.emit('createUserEventName', userData)

then all listeners and events receivers that listen on it will be invoked.

/eventrix/index.js
const createUserListener = (userData) => { // do something with userData };
eventrix.listen('createUserEventName', createUserListener);

eventrix.emit('createUserEventName', { name: 'Max' });
// after call emit createUserListener will be called with { name: 'Max' } as userData

State management

Now we know how to listen on events and emit events. But what about state? Where is it? If we want to change state, we need to use events receiver

/eventrix/receivers.js
import { EventsReceiver } from 'eventrix';

const createUserReceiver = new EventsReceiver('createUser', (eventName, userData, stateManager) => {
    const users = stateManager.getState('users');
    stateManager.setState('users', [...users, userData]);  
});
/eventrix/index.js
eventrix.useReceiver(createUserReceiver)

If you use EventsReceiver in eventrix instance, it will get access to stateManager when event will be called. State manager can set new state or get any state which you need.

Eventrix has one default events receiver on event setState. When you emit this event with

{ stateName: 'users', value: [{ name: 'Max' }] }

it will set state users to

[{ name: 'Max' }]
eventrix.emit('setState', { stateName: 'users', value: [{ name: 'Max' }] });
// after call emit stateManager will set new value to state `users` 

Fetch with Eventrix

If you want to use fetch or axios with eventrix you can do this with EventsReceiver and emit

import { EventsReceiver } from 'eventrix';
import axios from 'axios';

const fetchReceiver = new EventsReceiver('fetchUsers', (eventName, eventData, stateManager) => {
  return axios.get('https://somedomain.com/users', { params: eventData })
              .then(({ data: users }) => {
                stateManager.setState('users', users);
                eventrix.emit('fetchUsers.success', users);
              })
              .catch((error) => {
                stateManager.setState('users', []);
                eventrix.emit('fetchUsers.error', error);
              });
});

eventrix.useReceiver(fetchReceiver);

eventrix.emit('fetchUsers', { search: 'johny' });
// after emit event a `fetchUsers` the `fetchReceiver` will get users data from server and put it to `users` state

When you want to fetch some data and put it to state, you can also use special helper function called fetchToStateReceiver

import { fetchToStateReceiver } from 'eventrix';
import axios from 'axios';

const fetchReceiver = fetchToStateReceiver('fetchUsers', 'users', (eventData, eventrixState, emit) => {
  return axios.get(https://somedomain.com/users, { params: eventData })
              .then(({ data: users }) => users)
              .catch((error) => []);
});

eventrix.useReceiver(fetchReceiver);

eventrix.emit('fetchUsers', { search: 'johny' });

Hmmm what is the difference ? You define state path and promise must return new state. Take a look on more complex example with users manage class.

import { fetchToStateReceiver } from 'eventrix';

class usersService {
  constructor(axios, eventrix) {
    this.axios = axios;
    this.eventrix = eventrix;
    
    this.getUsers = this.getUsers.bind(this);
    this.create = this.create.bind(this);
    
    this.eventrix.useReceiver(fetchToStateReceiver('users:getUsers', 'users', this.getUsers));
    this.eventrix.useReceiver(fetchToStateReceiver('users:create', 'users', this.create));
  }
  getUsers(filters, state, emit) {
    return axios.get('https://somedomain.com/users', { params: filters })
                .then(({ data }) => {
                  return data
                })
                .catch(error => {
                  return []
                });
  }
  create(user, state, emit) {
    return axios.post('https://somedomain.com/users', user)
                .then(({ data }) => {
                  return [data, ...state.users];
                });
  }
}

Fetch error handling with Eventrix

You can handle error using catch on promise as we do in fetch with eventrix section or use fetchHandler. Fetch handler is small midlerware for fetch method that emit events when fetch success or fetch error.

import { fetchToStateReceiver, fetchHandler } from 'eventrix';
import axios from 'axios';

const fetchUsersMethod = (eventData, eventrixState, emit) => {
  return axios.get(https://somedomain.com/users, { params: eventData })
              .then((users) => {
                emit('fetchUsers.success', users);
                return users
              })
              .catch((error) => {
                emit('fetchUsers.error', error);
              });
};

const fetchUserMethodWithHandler = fetchHandler(
  fetchUsersMethod,
  {
    success: {
      eventName: 'fetchUsers.success',
      data: 'Users list loaded',
    },
    error: {
      eventName: 'fetchUsers.failed',
      data: 'Users list load failed',
    },
  }
);

const fetchReceiver = fetchToStateReceiver('fetchUsers', 'users', fetchUserMethodWithHandler);

eventrix.useReceiver(fetchReceiver);

eventrix.emit('fetchUsers', { search: 'johny' });

If you want create event data basing on response, error, or receiver event data you can use getData attribute on success and error.

{
    success: {
      eventName: 'fetchUsers.success',
      getData: (response, eventData) => `Users list loaded with resoults for "${eventData.search}"`,
    },
    error: {
      eventName: 'fetchUsers.failed',
      getData: (error, eventData) => `Users list load failed when search "${eventData.search}"`,
    },
}

React

Now we know how to send events, manage state and send requests to server with Eventrix. What about React and components? Eventrix has react section which gives us react context, provider, hocs and hooks.

Context

import { EventrixContext, EventrixProvider } from 'eventrix';

Hooks

import {
useEventrixState, // get and set state similar to react but from eventrix store
useEmit,          // emit events
useEvent,         // register event listeners
useEventState,    // cache events and store last one in state
useFetchToState   // save request response in eventrix state
} from 'eventrix';

HOCs

import {
withEventrixState, // get state from store to 
withEventrix,
} from 'eventrix';

If you want to use Eventrix in Your react project you must use EventrixProvider on the top of the project components tree.

import React from "react";
import ReactDOM from "react-dom";
import { EventrixProvider } from 'eventrix';

import eventrix from './eventrix';
import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <EventrixProvider eventrix={eventrix}>
    <App />
  </EventrixProvider>,
  rootElement
);

When we have EventrixProvider all of HOCs and hooks can be used in project.

HOCs

Eventrix has two HOCs withEventrix and withEventrixState. First HOC pass eventrix instance to props.

import React from 'react';
import { withEventrix } from 'eventrix';

class UsersList extends React.Component {
  removeUser = (user) => {
    this.props.eventrix.emit('removeUser', user);
  }
  render() {
    const { users } = this.props;
    return (
      <div>
        {users.map(user => 
          <div>
            {user.name} <button onClick={() => this.removeUser(user)}>remove user</button>
          </div>
        )}
      </div>
    )
  } 
}

export default withEventrix(UsersList);

Second HOC give you posibility to listen on selected state change and pass this state to props. Component will be rerender when state will be changed.

import React from 'react';
import { withEventrixState } from 'eventrix';

class UsersList extends React.Component {
  render() {
    const { users } = this.props;
    return (
      <div>
        {users.map(user => 
          <div>
            {user.name}
          </div>
        )}
      </div>
    )
  } 
}

export default withEventrixState(UsersList, ['users']);

If you want to map state to props You can use third argument mapStateToProps.

export default withEventrixState(UsersList, ['users'], (state, props) => { usersList: state.users });

What can I do when I need data from props to define stateNames? You can pass function as a second argument.

export default withEventrixState(
  UsersDetails,
  (props) => [`usersById.${props.userId}`],
  (state, props) => { userData: state[`usersById.${props.userId}`] }
);

Hooks

Eventrix gives us special react hooks for state and events management. For example if You want to rerender component when some state will be changed, you can use useEventrixState hook.

import React from 'react';
import { useEventrixState } from 'eventrix';

const UsersList = () => {
  const [users, setUsers] = useEventrixState('users');
  return (
    <div>
      {users.map(user => 
        <div>
          {user.name}
        </div>
      )}
    </div>
  );
}

useEventrixState hook also give you posibility to update used state. This hook is similar to useState but don’t have initial value. Remember, this is eventrix state and you can share this state with other component in Your app.

These two components use the same state

import React from 'react';
import { useEventrixState } from 'eventrix';

const UsersList = () => {
  const [users, setUsers] = useEventrixState('users');
  return (
    <div>
      {users.map(user => 
        <div>
          {user.name}
        </div>
      )}
    </div>
  );
}
import React from 'react';
import { useEventrixState } from 'eventrix';

const UsersCounter = () => {
  const [users] = useEventrixState('users');
  return (
    <div>
      {users.length}
    </div>
  );
}

When we need to listen on some event and do some action, we can use useEvent hook.

import React from 'react';
import Loader from './Loader';
import { useEvent, useEventrixState } from 'eventrix';

const UsersList = () => {
  const [users] = useEventrixState('users');
  const [isLoading, setIsLoading] = useState(false);
  useEvent('users:load', (eventData) => setIsLoading(true));
  useEvent('users:load,success', (eventData) => setIsLoading(false));
  if (isLoading) {
    return <Loader />;
  }
  return (
    <div>
      {users.map(user => 
        <div>
          {user.name}
        </div>
      )}
    </div>
  );
}

This example showed us two situations:

  • displaying loader when users list is loading

  • removing loader when users list is loaded successfully.

Sometimes we need to emit some events from components. For this purpose Eventrix has useEmit hook that gives you access to eventrix.emit method.

import React from 'react';
import Loader from './Loader';
import { useEmit, useEventrixState } from 'eventrix';

const UsersList = () => {
  const [users] = useEventrixState('users');
  const emit = useEmit();
  if (isLoading) {
    return <Loader />;
  }
  return (
    <div>
      {users.map(user => 
        <div>
          {user.name} <button onClick={() => emit('removeUser', user)}>remove user</button>
        </div>
      )}
    </div>
  );
}

Last updated