redraw

Types

Deprecated: `Component` type has been removed from Redraw. Use `Element` instead.

Components are deprecated, as they don’t represent a component in React. React defines components as function that generates elements, while Redraw was defining components as the result of component running.
Currently, thinking with Component can be harmful, to understand the difference between plain elements and components.
Use Element type instead.

As a matter of backward compatibility, Component is now aliased to Element, and will be removed in further versions.

pub type Component =
  Element

Pass data without props drilling.
Documentation

pub type Context(a)

React has two notions built-in: elements and components. Element is sometimes referred as JSX or JSX.Element in other React bindings.
An Element can be a DOM node or the result of the execution of a component. Any time you need to draw on the screen, whether it’s in the DOM or on your smartphone screen with React Native, you need to return an Element.

Be careful, a “component” in React is a function which can create elements. For example, fn () -> Element is a so-called “component”. Contrarily to elements, a component can hold internal state, have a lifecycle and can run side-effects. On the opposite, elements do not hold state, and cannot have side-effects.

Creating an Element is as simple as calling functions from redraw/dom/html, while creating components can be done with component_.

pub type Element

Main error type.

pub type Error {
  ExistingContext(name: String)
  UnknownContext(name: String)
  DevelopmentOnly
  OwnerStackUnavailable
}

Constructors

  • ExistingContext(name: String)

    Error returned from create_context_. Context with the corresponding name already exists.

  • UnknownContext(name: String)

    Error returned from get_context. Context with the corresponding name does not exists.

  • DevelopmentOnly

    Error returned from capture_owner_stack. capture_owner_stack can only be used in development, and you’re not in development anymore.

  • OwnerStackUnavailable

    Error returned from capture_owner_stack. capture_owner_stack can sometimes return null, when the stack is unavailable.

In previous versions of redraw, Componentwas used to designateElement. In newer versions of redraw, Componentis kept for backward-compatibility, andReactComponentis used instead.ReactComponentwill be removed in favour ofComponent` when the future version of React will be live.

A ReactComponent represents a function, accepting inputs (i.e. props) and returning React elements. A ReactComponent is the only place that can hold state in a React component-tree. It has a lifecycle and can run side-effects.

Defining ReactComponent is achieved using redraw.component_, and they can be used with redraw.compose. redraw.compose makes sure no components can be used outside of the bootstrap phase of Redraw.

pub opaque type ReactComponent(props)

Deprecated: Use `ref.Ref(a)` instead.

pub type Ref(a) =
  ref.Ref(a)
pub type Suspense {
  Suspense(fallback: Element)
}

Constructors

Values

pub fn act(
  act_fn: fn() -> promise.Promise(Nil),
) -> promise.Promise(Nil)

Test helper to apply pending React updates before making assertions.
Documentation

pub fn capture_owner_stack() -> Result(String, Error)

Reads the current Owner Stack in development and returns it as a string if available.

Owner Stacks are available in

  • Component render
  • Effects (e.g. use_effect)
  • React’s event handlers (e.g. button([a.on_click(fn (_) {...})]))
  • React error handlers (React Root options onCaughtError, onRecoverableError, and onUncaughtError)

If no Owner Stack is available, null is returned (see Troubleshooting: The Owner Stack is null).

Owner Stacks are only available in development. captureOwnerStack will always return null outside of development.

Documentation

pub fn component(
  name name: String,
  render render: fn(props, children) -> Element,
) -> fn(props, children) -> Element

Deprecated: Components in Redraw have changed. Use `component_` instead.

Create a Redraw component, with a name, and a render function. render will accept props, and a list of children.

pub fn component_(
  name name: String,
  render render: fn(props) -> Element,
) -> ReactComponent(props)

Create a Redraw component, with a name, and a render function. A render function is simply a function accepting props, and returning an Element.

pub fn my_component() {
  // Create your other Redraw components before defining the others.
  use my_other_component <- redraw.compose(my_other_component())
  // Define your component.
  use props: MyComponentProps <- redraw.component_("MyComponent")
  // Define some hooks.
  let #(state, set_state) = redraw.use_state(0)
  // Return an `Element`. Keep in mind an empty element can be defined
  // with `component_` with `Nil` as props.
  html.div([], [
    html.text("In my component"),
    my_other_component(MyOtherComponentProps),
  ])
}
pub fn compose(
  component: ReactComponent(props),
  return: fn(fn(props) -> Element) -> ReactComponent(p),
) -> ReactComponent(p)

Allow to compose and use other components within a component. compose is the only way to use components with other components. compose should be called during the bootstrap phase of redraw, and let you access the underlying component.

fn component() {
  use other_component <- redraw.compose(other_component())
  h.div([], [
    other_component(),
    h.div([], []),
  ])
}
pub fn context(
  name: String,
  default_value: fn() -> a,
) -> Context(a)

Deprecated: Named contexts are not part of Redraw anymore.

context emulates classic Context usage in React. Instead of calling create_context and get_context, it’s possible to simply call context, which will get or create the context directly, and allows to write code as if Context is globally available. context also tries to preserve type-checking at most. context.default_value is lazily evaluated, meaning no additional computations will ever be run.

import redraw

const context_name = "MyContextName"

pub type MyContext {
  MyContext(count: Int, set_count: fn (Int) -> Nil)
}

fn default_value() {
  let count = 0
  les set_count = fn (_) { Nil }
  MyContext(count:)
}

pub fn provider() {
  use _, children <- redraw.component()
  let context = redraw.context(context_name, default_value)
  let #(count, set_count) = redraw.use_state(0)
  redraw.provider(context, MyContext(count:, set_count:), children)
}

pub fn use_my_context() {
  let context = redraw.context(context_name, default_value)
  redraw.use_context(context)
}

context should never fail, but it can be wrong if you use an already used name.

pub fn create_context(
  name: String,
  default_value: a,
) -> Result(Context(a), Error)

Deprecated: Named contexts are not part of Redraw anymore. Use `create_context_` instead.

Create a context that components can provide or read. Each context is referenced by its name, a little bit like actors in OTP (if you’re familiar with Erlang). Because Gleam cannot execute code outside of main function, creating a context should do some side-effect at startup.

In traditional React code, Context usage is usually written like this.

import * as react from 'react'

// Create your Context in a side-effectful way.
const MyContext = react.createContext(defaultValue)

// Create your own provider, wrapping your context.
export function MyProvider(props) {
  return <MyContext.Provider>{props.children}</MyContext.Provider>
}

// Create your own hook, to simplify usage of your context.
export function useMyContext() {
  return react.useContext(MyContext)
}

To simplify and mimic that usage, Redraw wraps Context creation with some caching, to emulate a similar behaviour.

import redraw

const context_name = "MyContextName"

pub fn my_provider(children) {
  let assert Ok(context) = redraw.create_context(context_name, default_value)
  redraw.provider(context, value, children)
}

pub fn use_my_context() {
  let assert Ok(context) = redraw.get_context(context_name)
  redraw.use_context(context)
}

Be careful, create_context fails if the Context is already defined. Choose a full qualified name, hard to overlap with inattention. If you want to get a Context in an idempotent way, take a look at context().

Documentation

pub fn create_context_(default_value: a) -> Context(a)

Create a context that components can provide or read. Because Gleam cannot execute code outside of main function, creating a context should do some side-effect at startup.

In traditional React code, Context usage is usually written like this.

import * as react from 'react'

// Create your Context in a side-effectful way.
const MyContext = react.createContext(defaultValue)

// Create your own provider, wrapping your context.
export function MyProvider(props) {
  return (
    <MyContext.Provider>
      {props.children}
    </MyContext.Provider>
  )
}

// Create your own hook, to simplify usage of your context.
export function useMyContext() {
  return react.useContext(MyContext)
}

In Redraw, creating your application goes through a bootstrap phase, where every component gets created. Your context should be passed at that moment. Find an example with React DOM.

import redraw
import redraw/dom/client

type MyContext {
  MyContext(
    name: String,
  )
}
pub fn main() {
  let assert Ok(root) = client.create_root("#app")
  let context = redraw.create_context_(MyContext(name: "Louis"))
  let app = app(context)
  client.render(root, {
    redraw.strict_mode([
      redraw.provider(context, value, [
        app(AppProps(name: "Lucy")),
      ])
    ])
  })
}

type AppProps {
  AppProps(
    name: String,
  )
}

fn app(ctx: Context(a)) -> redraw.Component(AppProps) {
  use props: AppProps <- redraw.element("App")
  let context = redraw.use_context(ctx)
  html.div([], [
    html.text("Hello " <> props.name <> "!")
    html.text("Hello " <> context.name <> "!")
  ])
}

Documentation

pub fn element(
  name name: String,
  render render: fn(c) -> Element,
) -> fn(c) -> Element

Deprecated: Components in Redraw have changed. Use `component_` instead.

pub fn fragment(children: List(Element)) -> Element

Fragment allow to group children, without creating a node in the DOM.
Documentation

pub fn get_context(name: String) -> Result(Context(a), Error)

Deprecated: Named contexts are not part of Redraw anymore.

Get a context. Because of FFI, get_context breaks the type-checker. It should be considered as unsafe code. As a library author, never exposes your context and expect users will call get_context themselves, but rather exposes a use_my_context() function, handling the type-checking for the user.

import redraw

pub type MyContext {
  MyContext(value: Int)
}

/// `use_context` returns `Context(a)`, should it can be safely returned as
/// `Context(MyContext)`.
pub fn use_my_context() -> redraw.Context(MyContext) {
  let context = case redraw.get_context("MyContextName") {
    // Context has been found in the context cache, use it as desired.
    Ok(context) -> context
    // Context has not been found. It means the user did not initialised it.
    Error(_) -> panic as "Unitialised context."
  }
  redraw.use_context(context)
}
pub fn get_current(from ref: ref.Ref(a)) -> a

Deprecated: Refs now have their dedicated module. Use `ref.current` instead.

Get the current value of a ref.

pub fn keyed(
  element: fn(List(Element)) -> Element,
  content: List(#(String, Element)),
) -> Element

Redraw does not support passing key element to components in an easy way like React does. To simplify this, it uses the same API than Lustre to put keys on children.

fn my_component(props, children) {
  redraw.keyed(my_other_component(props, _), {
    use item <- list.map(children)
    #("my-key", item)
  })
}
pub fn memo(
  component: fn(props, List(Element)) -> Element,
) -> fn(props, List(Element)) -> Element

Deprecated: Memoization should be done with `memoize_`.

Memoizes a Redraw component with children.
Documentation

pub fn memo_(
  component: fn(props) -> Element,
) -> fn(props) -> Element

Deprecated: Memoization should be done with `memoize_`.

Memoizes a Redraw component without children.
Documentation

pub fn memoize(
  render: fn(props) -> Element,
) -> fn(props) -> Element

Deprecated: Components in Redraw have changed. Use `memoize_` instead, in conjunction with `component_`.

pub fn memoize_(
  component: ReactComponent(props),
) -> ReactComponent(props)

Accepts a Component, and wrapp it in React.memo. React.memo ensures that a component will never repaint when props are identical between two renders. Redraw applies an algorithm suited to Gleam, to avoid you the hard task of comparing props, while providing a correct comparison algorithm.

While such optimisation could be done for every component, being able to select if a component should be memoized or not can help finding and solving bugs. Take a look at the React documentation to get a better idea if you need memoization or not.

pub fn my_component() {
  // Create your other Redraw components before defining the others.
  use my_other_component <- redraw.compose(my_other_component())
  // Define your component.
  redraw.component_("MyComponent", fn (props: MyComponentProps) {
    // Define some hooks.
    let #(state, set_state) = redraw.use_state(0)
    // Return an `Element`. Keep in mind an empty element can
    // be defined with `memoized` with `Nil` as props.
    html.div([], [
      html.text("In my component"),
      my_other_component(MyOtherComponentProps),
    ])
  })
  |> redraw.memoize_
}
pub fn profiler(children: List(Element)) -> Element

Profile allows to measure code performance for a component tree.
Documentation

pub fn provider(
  context context: Context(a),
  value value: a,
  children children: List(Element),
) -> Element

Wrap your components into a context provider to specify the value of this context for all components inside.
Documentation

pub fn set_current(of ref: ref.Ref(a), with value: a) -> Nil

Deprecated: Refs now have their dedicated module. Use `ref.assign` instead.

Set the current value of a ref, overriding its existing content.

pub fn standalone(
  name name: String,
  render render: fn() -> Element,
) -> fn() -> Element

Deprecated: Components in Redraw have changed. Use `component_` instead.

Create a Redraw standalone component, with a name and a render function. Keep in mind this component does not accept children nor props.

pub fn start_transition(scope scope: fn() -> Nil) -> Nil

Let you update the state without blocking the UI.
Documentation

pub fn strict_mode(children: List(Element)) -> Element

Strict Mode should be enabled during development.
Documentation

pub fn suspense(
  props: Suspense,
  children: List(Element),
) -> Element

Suspense allow to display a fallback content while waiting for children to finish loading.
Documentation

pub fn to_component(
  name name: String,
  component render: fn(props) -> Element,
) -> fn(props, children) -> Element

Deprecated: Converting React components to Redraw components are most of the time done by hand and cannot automatised.

Convert a React component to a React-redraw component with children. Give it a name, and send directly the FFI. Don’t worry about the snake_case over camelCase, redraw take care of it for you.

Because children can accept anything, it’s up to you to define the type of the children. If you send a List, it will be automatically converted to a JavaScript array.

import redraw

pub type MyComponentProps {
  MyComponentProps(
    first_prop: Bool,
    second_prop: String,
  )
}

@external(javascript, "my_library", "MyComponent")
fn do_my_component(props: MyComponentProps) -> redraw.Component

pub fn my_component() -> fn(MyComponentProps, List(Element)) -> redraw.Component {
  redraw.to_component("MyComponent", do_my_component)
}
pub fn to_element(
  name name: String,
  component render: fn(props) -> Element,
) -> fn(props) -> Element

Deprecated: Converting React components to Redraw components are most of the time done by hand and cannot automatised.

Convert a React Component to a Redraw Element. Give it a name, and send directly the FFI. Don’t worry about the snake_case over camelCase, redraw take care of it for you.

import redraw

pub type MyComponentProps {
  MyComponentProps(
    first_prop: Bool,
    second_prop: String,
  )
}

@external(javascript, "my_library", "MyComponent")
fn do_my_component(props: MyComponentProps) -> redraw.Component

pub fn my_component() -> fn(MyComponentProps) -> redraw.Component {
  redraw.to_element("MyComponent", do_my_component)
}
pub fn use_action_state(
  action: fn(state, payload) -> Nil,
  initial_state: state,
) -> #(state, fn(payload) -> nil, Bool)

Allow you to update state based on the result of a form action.
Documentation

pub fn use_async_transition() -> #(
  Bool,
  fn() -> promise.Promise(Nil),
)

Async variant of use_transition.
Documentation

pub fn use_callback(
  fun: function,
  dependencies: dependencies,
) -> function

Let you cache a function definition between re-renders. dependencies should be a tuple.
Documentation

pub fn use_context(context: Context(a)) -> a

Let you read and subscribe to context from your component.

Under-the-hood, use_context uses the new use function. This means you can call use_context conditionnally! \

Documentation

pub fn use_debug_value(value: a) -> Nil

Let you add a label to a custom Hook in React DevTools.
Documentation

pub fn use_debug_value_(
  value: a,
  formatter: fn(a) -> String,
) -> Nil

Let you add a label to a custom Hook in React DevTools, but allow to format it before.
Documentation

pub fn use_deferred_value(value: a) -> a

Let you defer updating a part of the UI.
Documentation

pub fn use_effect(value: fn() -> Nil, dependencies: a) -> Nil

Let you synchronize a component with an external system.
Documentation

pub fn use_effect_(
  value: fn() -> fn() -> Nil,
  dependencies: a,
) -> Nil

Let you synchronize a component with an external system. Allow to return a cleanup function.
Documentation

pub fn use_id() -> String

Generate unique IDs that can be passed to accessibility attributes.
Documentation

pub fn use_imperative_handle(
  ref: ref.Ref(option.Option(a)),
  handler: fn() -> a,
  dependencies: b,
) -> Nil

Let you customize the handle exposed as a ref. Use use_imperative_handle when you want to customize the data stored in a ref.
Documentation

pub fn use_imperative_handle_(
  ref: ref.Ref(a),
  handler: fn() -> a,
  dependencies: b,
) -> Nil

Let you customize the handle exposed as a ref. You should probably use use_imperative_handle by default with optional refs.
Documentation

pub fn use_insertion_effect(
  handler: fn() -> Nil,
  deps: deps,
) -> Nil

Allow inserting elements into the DOM before any layout Effects fire.

use_insertion_effect is for CSS-in-JS library authors. Unless you are working on a CSS-in-JS library and need a place to inject the styles, you probably want use_effect or use_layout_effect instead.

Documentation

pub fn use_insertion_effect_(
  handler: fn() -> fn() -> Nil,
  deps: deps,
) -> Nil

Allow inserting elements into the DOM before any layout Effects fire and allow to return a cleanup function.

use_insertion_effect is for CSS-in-JS library authors. Unless you are working on a CSS-in-JS library and need a place to inject the styles, you probably want use_effect or use_layout_effect instead.

Documentation

pub fn use_layout_effect(
  value: fn() -> Nil,
  dependencies: a,
) -> Nil

Version of useEffect that fires before the browser repaints the screen.
Documentation

pub fn use_layout_effect_(
  value: fn() -> fn() -> Nil,
  dependencies: a,
) -> Nil

Version of useEffect that fires before the browser repaints the screen and allow to return a cleanup function.
Documentation

pub fn use_lazy_state(
  initial_value: fn() -> a,
) -> #(a, fn(a) -> Nil)

Let you add a state variable to your component. Allow to create the initial value in a lazy way.
Documentation

pub fn use_lazy_state_(
  initial_value: fn() -> a,
) -> #(a, fn(fn(a) -> a) -> Nil)

Let you add a state variable to your component. Allow to create the initial value in a lazy way. Give an updater function instead of a state setter.
Documentation

pub fn use_memo(calculate_value: fn() -> a, dependencies: b) -> a

Let you cache the result of a calculation between re-renders.
Documentation

pub fn use_optimistic(state: state) -> #(state, fn(state) -> Nil)

Let you optimistically update the UI.
Documentation

pub fn use_optimistic_(
  state: state,
) -> #(state, fn(fn(state) -> state) -> Nil)

Let you optimistically update the UI.
Documentation

pub fn use_optimistic_action(
  state: state,
  update: fn(state, action) -> state,
) -> #(state, fn(action) -> Nil)

Let you optimistically update the UI.
Documentation

pub fn use_promise(promise: promise.Promise(state)) -> state

Wait for a Promise and returns its content. Uses use under-the-hood. When the Promise is loading, it fallbacks to the nearest Suspense boundary.
Documentation

pub fn use_reducer(
  reducer: fn(state, action) -> state,
  initial_state: state,
) -> #(state, fn(action) -> Nil)

Let you add a reducer to your component.
Documentation

pub fn use_reducer_(
  reducer: fn(state, action) -> state,
  initializer: initializer,
  init: fn(initializer) -> state,
) -> #(state, fn(action) -> Nil)

Let you add a reducer to your component. Allow to initialize the store in a custom way.
Documentation

pub fn use_ref() -> ref.Ref(option.Option(a))

Let you reference a value that’s not needed for rendering. Most used ref you’ll want to create. They’re automatically created to None, and can be passed to ref prop or use_imperative_handle. Ref are most of the time used to reference DOM node, in order to run side-effects. In this case, you probably don’t want the ref value to be anything than Option(a), unless you need to persist a value across renders which is not part of the DOM. In that case, use use_ref_.
Documentation

pub fn use_ref_(initial_value: a) -> ref.Ref(a)

Let you reference a value that’s not needed for rendering. Use use_ref if you’re trying to acquire a reference to a child or to a component. Use use_ref_ when you want to keep track of a data, like if you’re doing some side-effects, in conjuction with ref.current and ref.assign.
Documentation

pub fn use_state(initial_value: a) -> #(a, fn(a) -> Nil)

Let you add a state variable to your component.
Documentation

pub fn use_state_(
  initial_value: a,
) -> #(a, fn(fn(a) -> a) -> Nil)

Let you add a state variable to your component. Give an updater function instead of a state setter.
Documentation

pub fn use_sync_external_store(
  subscribe: fn(fn() -> Nil) -> fn() -> Nil,
  get_snapshot: fn() -> snapshot,
) -> snapshot

Let you subscribe to an external store.
Documentation

pub fn use_transition() -> #(Bool, fn() -> Nil)

Let you update the state without blocking the UI.
Documentation

Search Document