Skip to content

Latest commit

 

History

History
213 lines (147 loc) · 5.17 KB

react.md

File metadata and controls

213 lines (147 loc) · 5.17 KB

🔆 React example

🛠 setup

Here is the way to follow to setup La Taverne on your app:

  • 1: Write reactions: the way your "taverne" must reduce actions.
  • 2: Pass your instance {dispatch, taverne} through the provider <Taverne>.
  • 3: pour data in your containers
  • 4: dispatch actions to trigger reactions.
🔍 Illustration

In the following, let's illustrate how to use Taverne with:

  • a taverne of Items and its fetch function
  • a container plugged to this taverne,
  • and the component rendering the list of items.

Illustration will be marked with 🔍

📦 create a barrel

You'll have to define a barrel with

  • an initialState,
  • and a list of reactions.
const items = {
  initialState: {},
  reactions: []
};

export default items;

You should use one barrel for each feature.

La Taverne will:

  • handle an immutable nested state with Immer,
  • listen to actions to trigger the appropriate reactions,
  • emit updates to the containers only when there are changes they need.

computeAction

🔍 Example

Here is the example for our illustrating items

/* ./features/items/taverne.js */
import apiCall from './fetch-items.js';

const FETCH_ITEMS = 'FETCH_ITEMS';

const initialState = {items: null};

const fetchItems = {
  on: FETCH_ITEMS,
  perform: async (parameters, dispatch, getState) => {
    // This function will be called whenever {type:FETCH_ITEMS} is dispatched.
    // `getState` is provided here for convenience, to access the current taverne state.

    const items = await apiCall(parameters);
    return items;
  },
  reduce: (draft, payload) => {
    // 'reduce' will be called after `perform` is over.
    // 'perform' returns the items, so here payload === items
    draft.items = payload;
  }
};

const reactions = [fetchItems];

export default {initialState, reactions};
export {FETCH_ITEMS};

🐿️ setup the <Taverne> provider

Once all barrels are ready, instanciate and pass {dispatch, taverne} to the <Taverne> provider.

🔍 Example:

/* ./index.js */
import React from 'react';
import {render} from 'react-dom';
import {Taverne} from 'taverne/hooks';

import items from './barrels/items.barrel.js';
import anyOtherStuff from './barrels/whatever.barrel.js';

const {dispatch, taverne} = createLaTaverne({
  items,
  anyOtherStuff
});

render(
  <Taverne dispatch={dispatch} taverne={taverne}>
    <App />
  </Taverne>,
  container
);

🍺 Pouring your global state to your containers

storeState ➡️ props

The pour hook allows to listen to changes in your global state, use the part you need in your local props.

🔍 Here is the example for our illustrating items:

/* ./features/items/container.js */

import React from 'react';
import ItemsComponent from './component';

const ItemsContainer = props => {
  const {pour} = useTaverne();
  const items = pour('items');

  return <ItemsComponent items={items} />;
};

To listen to specific changes in the global state, and update your local props only on those changes, you can use many kinds of parameters to pour() (see the advanced section).

📡 dispatching actions

Use prop drilling from your containers to your components: pass functions dispatching the actions

import {SELECT_ITEM} from './barrels/items.barrel.js';

const ItemsContainer = props => {
  const {dispatch} = useTaverne();

  const selectItem = id => () => {
    dispatch({
      type: SELECT_ITEM,
      itemId: id
    });
  };

  return <ItemsComponent selectItem={selectItem} />;
};

🔥 advanced usage

local rendering

The whole point of Taverne is to be able to perform extremely local rendering.

So, rather than the listening for the whole state updates, you can update rendering depending on specific updates in your global state.

To do so, specify the props mapping you want to listen for changes, telling corresponding paths in your state.

const Container = () => {
  const {pour} = useTaverne();
  const foo = pour('path.to.anything.within.your.taverne');

  return <Component foo={foo} />;
};

This way, on every taverne update, specific props will be extracted for the components.

If those props don't change, the taverne won't notify your container, preventing a re-rendering: this will allow accurate local rendering from a global app state.

more pouring facilities

You can map a many props in one single pouring:

const ItemsContainer = props => {
  const {pour} = useTaverne();
  const {items, other} = pour({
    items: 'items',
    other: 'plop.plip.plup'
  });

  return <ItemsComponent items={items} other={other} />;
};

You can also use your state to read paths depending on your state:

const BookContainer = props => {
  const {pour} = useTaverne();
  const book = pour(state => ({
    book: `shelves.${state.selectedShelfId}.books.${state.selectedBookId}`
  }));

  return <BookComponent book={book} />;
};

cheers 🍻