RedwoodJS

v0.21.0

# Flash: Messaging Bus

Pass temporary message objects between components.

FlashContext was created with post-action messaging in mind. Flash is particularly useful, for instance, when we want to alert the user that something they tried was successful or erroneous.

Flash makes use of React's Context API to make it easy to pass message objects between components, even deeply nested ones.

The Flash component provides a typical message display unit for rendering the messages provided to FlashContext.

# FlashContext

FlashContext is the headquarters for Redwood's Flash system. Its provider component is packaged with the <RedwoodProvider /> component which makes it ready to use out of the box. Your components can pass (think, "send and receive") message objects by subscribing to it via the provided useFlash hook.

FlashContext provides update functions which allow subscribed components to send and manipulate message objects. Subscribers can consume, add, dismiss, and cycle messages.

# Consume messages

Of course, we'll want a way to display messages to the user. That's easy:

import { useFlash } from '@redwoodjs/web'

const MyFlashComponent = () => {
  // messages, an array of message objects
  const { messages } = useFlash()

  // if the messages array is empty
  // return nothing
  if (!messages.length) {
    return null
  }

  return (
    <>
      {messages.map((msg) => (
        <div key={msg.id} className={msg.classes}>
          {msg.text}
        </div>
      ))}
    </>
  )
}

# Add a message

...but there aren't any messages to display yet. Let's add one:

import { useFlash } from '@redwoodjs/web'

const MyMessageButton = () => {
  // addMessage(text, options)
  const { addMessage } = useFlash()

  const sayHello = () => {
    addMessage("Hello world!", {
      classes: "success-style"
    })
  }

  return (
    <>
      <button onClick={() => sayHello()}>
    </>
  )
}

addMessage() takes two parameters; text (string) and options (object)

The options object has the following structure:

{
  text: "It is your birthday period", // overwrites the text param passed to addMessage()
  classes: "bold red text", // css classes - generally recommended approach
  style: { fontWeight: 'bold', color: 'red' }, // inline style object with camelCased keys
  persist: true, // true will persist the message through each cycle until it is dismissed manually
  viewed: false, // once true, the message will be dismissed on the next cycle
}

# Dismiss a message

We want to allow our user to dismiss the message manually.

import { useFlash } from '@redwoodjs/web'

const Message = ({ message }) => {
  const { dismissMessage } = useFlash()

  return (
    <div className={message.classes}>
      {message.text}
      <button onclick={() => dismissMessage(message.id)}>Dismiss</button>
    </div>
  )
}

# Cycle a message

It is up to the consumer component—and thusly, the developer—to dictate when and how a message should be dismissed. However, in most cases, a message's life cycle will include the following states: Not viewed (initial state), viewed, and dismissed. Typically the message moves on to the next phase as an effect of another action, say for instance, a route change. With this in mind, the cycleMessage() function makes it easy to cycle messages through these states without requiring direct user interaction.

# Example: Show and dismiss after three seconds

import { useFlash } from '@redwoodjs/web'

const Message = ({ message }) => {
  const { cycleMessage } = useFlash()

  React.useEffect(() => {
    // the message mounted so mark it as viewed
    cycleMessage(message.id)
    // after three seconds dismiss it
    setTimeout(() => {
      cycleMessage(message.id)
    }, 3000)
  }, [])

  return <div className={message.classes}>{message.text}</div>
}

# Example: Show and dismiss after the route changes

import { useFlash } from '@redwoodjs/web'

const Message = ({ message }) => {
  const { cycleMessage } = useFlash()
  const { path } = useLocation()

  React.useEffect(() => {
    // the message mounted so mark it as viewed
    cycleMessage(message.id)
    // cycle again (re-run useEffect) if path changes
  }, [path])

  return <div className={message.classes}>{message.text}</div>
}

# useFlash hook

useFlash is an abridgment of React.useContext(FlashContext). It allows you to subscribe to FlashContext's properties and functions from within any component nested within <RedwoodProvider />.

import { useFlash } from '@redwoodjs/web'

const { messages, addMessage, dismissMessage, cycleMessage } = useFlash()

# messages

messages = [
  {
    id: 0, // integer
    text: "Success!", // string
    classes: "green italic", // string
    style: { color: "green", fontStyle: "italic" } // object with camelCased keys
    persist: false, // boolean
    viewed: false, // boolean
  },
  {
    id: 1,
    ...
  }
]

# addMessage

let text = 'Success!' // string
let options = {
  classes: 'green italic', // string
  style: { color: "green", fontStyle: "italic" } // object with camelCased keys
  persist: false, // boolean
  viewed: false,
}
addMessage(text, options)

# dismissMessage

dismissMessage(id) // integer

# CycleMessage

cycleMessage(id) // integer

# Flash (the component)

<Flash /> is a canonical implementation of a message display component. It is included in the templates generated by scaffold where it is used to display messages upon successful create, update, and delete actions.

By default, each message is marked as viewed once it is initially rendered and then dismissed on any subsequent render, typically induced by a route change.

# timeout

<Flash timeout={3000} />

The component accepts a timeout prop; an integer interpreted as milliseconds. If a value is provided, the message will be dismissed (thusly, removed from the dom) after this time has lapsed.

Note: In the future, <Flash /> will receive a cycleDependency prop with which a cycle is triggered on a falsy comparison.