2.0.0 • Published 5 years ago

react-simred v2.0.0

Weekly downloads
1
License
MIT
Repository
github
Last release
5 years ago

react-simred NPM version Build Status Tests Coverage

React indings for Simred state management

If you're not sure what Simred is, visit the Simred project page for more information.

Table of Content


Installation

$ npm install --save react-simred

Usage

Hooks

Passing the store

import Simred from 'simred'
import { Provider } from 'react-simred'
import * as React from 'react'
import * as ReactDOM from 'react-dom'

import { App } from './components'
import { rootReducer } from './reducers'

const store = Simred.createStore(rootReducer)

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

useSelector(selector)

The useSelector() hook allows you to select the parts of the state you need.

import * as React from 'react'
import { useSelector } from 'react-simred'

export const UsernameComponent = (props) => {
  const user = useSelector(state => state.user)

  return <span>{user.firstname} {user.lastname}</span>
}

useActions(actionsSelector)

The useActions() hook allows you to get all or some of the actions in your store.

import * as React from 'react'
import { useSelector, useActions } from 'react-simred'

export const UsernameComponent = (props) => {
  const user = useSelector(state => state.user)
  const actions = useActions() // all actions in the store
  const userActions = useActions(actions => actions.user) // only userActions

  return <span>{user.firstname} {user.lastname}</span>
}

For Redux users

If you used React with Redux, it works the exact same way

Passing the store

import Simred from 'simred'
import { Provider } from 'react-simred'
import * as React from 'react'
import * as ReactDOM from 'react-dom'

import { App } from './components'
import { rootReducer } from './reducers'

const store = Simred.createStore(rootReducer)

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

Implementing Container Components

import * as React from 'react'
import { connect } from 'react-simred'
import { Component } from './components'

const mapStateToProps = (state) => {
  return { todos: state.todos }
}

const mapActionsToProps = (actions) => {
  return { actions: actions.todos }
}

const Container = connect(mapStateToProps, mapActionsToProps)(Component)

export Container

For the rest of the world

To keep help understanding how this works, we'll keep following the todo list example from the Simred project page. Make sure you've read the complete to-do list example before continuing.

This tutorial is heavily inspired by this redux article

Presentational and Container Components

The bindings separate Presentational and Container Components. This helps separate presentation code from the business logic and makes components easier to reuse.

Presentational ComponentsContainer Compoents
PurposeHow things look (markup, styles)How things work (fetching data, state updates)
Aware of SimredNoYes
To read dataReads data from propsSubscribes to Simred state
To change dataInvokes callbacks from propsCalls Simred actions

Designing components

The design is simple, we want to display a list of todos. On click, a todo is crossed as completed. We want a field to add a new todo, and a toggle to show all, only completed or remaining todos.

Presentational components
  • TodoList displays a list of todos props:
    • todos: Array list of todo items like {text, completed}
    • onTodoClick(id: number) callback called when a todo is clicked
  • TodoItem a single todo item: props:
    • text: string text to display
    • completed: boolean is whether or not the item has been crossed
    • onClick() callback call when a todo is clicked
  • Link a link with a callback props:
    • onClick() callback called when the link is clicked
  • Footer where the user changes the which todos to show
  • App root component rendering everything
Container compoenents
  • VisibleTodoList Filters todos according the current visiblity setting
  • FilterLink get the current visibilty filter and renders a link props:
    • filter: The visibility filter it represents
  • AddTodo Input field + "Add" button

Implementing components

From this spec it is not hard to implement these components

Presentationnal Components

components/TodoItem.js

import * as React from 'react'

export const Todo = ({ text, completed, onClick }) => {

  render (
    <li
    onClick={onClick}
    style={{
      textDecoration: completed ? 'line-through' : 'none'
    }}
  >
    {text}
  </li>
  )
}

components/TodoList.js

import * as React from 'react'
import { TodoItem } from './TodoItem'

export const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map((todo, index) => (
      <TodoItem key={index} {...todo} onClick={() => onTodoClick(index)} />
    ))}
  </ul>
)

components/Link.js

import * as React from 'react'

export const Link = ({ active, children, onClick }) => {
  if (active) {
    return <span>{children}</span>
  }

  return (
    <a
      href=""
      onClick={e => {
        e.preventDefault()
        onClick()
      }}
    >
      {children}
    </a>
  )
}

components/Footer.js

import * as React from 'react'
import { FilterLink } from './containers/FilterLink'
import { VisibilityFilters } from './filters'

export const Footer = () => (
  <p>
    Show: <FilterLink filter={VisibilityFilters.SHOW_ALL}>All</FilterLink>
    {', '}
    <FilterLink filter={VisibilityFilters.SHOW_REMAINING}>Remaining</FilterLink>
    {', '}
    <FilterLink filter={VisibilityFilters.SHOW_COMPLETED}>Completed</FilterLink>
  </p>
)
Container Components

Those are the components aware of the Simred state. react-simred provides a connect() function to hook up React components to the Simred state while preventing some unnecessary re-renders.

To use connect(), you need to define a special function called mapStateToProps that describes how to transform the current Redux store state into the props you want to pass to a presentational component you are wrapping. VisibleTodoList needs to calculate the todos to pass to TodoList in accordance with the visibilityFilter

import { VisibilityFilters } from './filter'

const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case VisibilityFilters.SHOW_COMPLETED:
      return todos.filter(t => t.completed)
    case VisibilityFilters.SHOW_ACTIVE:
      return todos.filter(t => !t.completed)
    case VisibilityFilters.SHOW_ALL:
    default:
      return todos
  }
}

const mapStateToProps = state => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

Container components can also call Simred actions. Same as mapStateToProps, you may define a mapActionsToProps that recieves all actions from the root reducer and returns the ones to pass to the container.

const mapActionsToProps = actions => {
  return {
    onTodoClick: actions.todos.toggle
  }
}

Finally, we create the VisibleTodoList by calling connect()

export const VisibleTodoList = connect(
  mapStateToProps,
  mapActionsToProps
)(TodoList)

And that is all there is to it.

Find the rest of the container components below: containers/FilterLink.js

import { connect } from 'react-simred'
import Link from '../components/Link'

const mapStateToProps = (state, ownProps) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  }
}

const mapActionsToProps = (actions, ownProps) => {
  return {
    onClick: () => {
      actions.visibilityFilter.set(ownProps.filter))
    }
  }
}

export const FilterLink = connect(
  mapStateToProps,
  mapDispatchToProps
)(Link)

containers/AddTodo.js

import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'

let AddTodo = ({ addTodo }) => {
  let input

  return (
    <div>
      <form
        onSubmit={e => {
          e.preventDefault()
          if (!input.value.trim()) {
            return
          }
          addTodo(input.value)
          input.value = ''
        }}
      >
        <input
          ref={node => {
            input = node
          }}
        />
        <button type="submit">Add Todo</button>
      </form>
    </div>
  )
}

export const AddTodo = connect(
  () => {},
  actions => ({ addTodo: actions.todos.add })
)(AddTodo)

Wrapping all container in a single component

components/App.js

import * as React from 'react'
import { Footer } from './Footer'
import { AddTodo } from '../containers/AddTodo'
import { VisibleTodoList } from '../containers/VisibleTodoList'

const App = () => (
  <div>
    <AddTodo />
    <VisibleTodoList />
    <Footer />
  </div>
)

export App

Passing the store to the application

The containers need to be allowed access to the Simred state and actions. The <Provider> component from react-simred "magically" gives access to the store to all "connected" containers

index.js

import * as React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-simred'
import Simred from 'simred'
import { rootReducer } from './reducers'
import { App } from './components/App'

const store = Simred.createStore(rootReducer)

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

And Voilà!

License

MIT © Gaël PHILIPPE

2.0.0

5 years ago

1.1.3

5 years ago

1.1.2

5 years ago

1.1.1

5 years ago

1.0.1

5 years ago

1.0.0

5 years ago