How to use React Context in ReScript

According to the documentation, this is a context file.

/** ContextProvider.re */
let themeContext = React.createContext("light");

include React.Context; // Adds the makeProps external
let make = React.Context.provider(themeContext);

But makeProps is not exported from this file as a result

<ContextProvider value="light">
...
</ContextProvider>

compiler throws an error as makeProps cant be found.
https://rescript-lang.org/try?code=PQKhAIGEHsDsBcCmAPeAFATtAbgSwCaIYB0Gi4IwAUADaLzjwAWiAtojAigwLzgBKiAIYBjeMRFkhSTklQAKAEQ1cAcybxFASgDcVKrlgiaAV0IDhY4rO47wwYOACC+fAGdGLcKyEBrRJjQAA4e3ESwQjS09N5+5HyCouI2qMRBWHiEGPLMbBxwcvC6QA

Yep, because you need a react.component wrapper and pass the provided prop down to the provider:

module ThemeContext = {
  let context = React.createContext("light")

  module Provider = {
    let provider = React.Context.provider(context)

    @react.component
    let make = (~value, ~children) => {
      React.createElement(provider, {"value": value, "children": children})
    }
  }
}

@react.component
let make = () => {
  <ThemeContext.Provider value="dark">
    <div/>
  </ThemeContext.Provider>
}
2 Likes

Any chance of seeing an example where useReducer is sent down context? :smile:
My incomplete example looks something like this so far:

module TodoContext = {
  let stateContext = React.createContext(Todo.initialState)
  let dispatchContext = React.createContext()

  module Provider = {
    let stateProvider = React.Context.provider(stateContext)
    let dispatchProvider = React.Context.provider(dispatchContext)

    @react.component
    let make = (~children) => {
      let (state, dispatch) = React.useReducer(Todo.reducer, Todo.initialState)

      let dispatchProviderComponent = React.createElement(
        dispatchProvider,
        {"value": dispatch, "children": children},
      )

      let stateProviderComponent = React.createElement(
        stateProvider,
        {"value": state, "children": children},
      )
    }
  }
}

Some questions:

  • Read somewhere that it’s better to send state and dispatch down two separate providers for re-rendering performance reasons. Is this correct?
  • What value do I pass when creating the context for dispatch, and also for state? Is the let stateContext = React.createContext(Todo.initialState) correct?
  • How do I nest the dispatchProviderComponent and the stateProviderComponent for the return?

Apologies for the long question! Very new to React and Rescript :slightly_smiling_face: Hope someone can help :sweat_smile:

I have created a Playground.

type action = Increment | Decrement

//STATE CONTEXT Component
module StateContext = {
  let context = React.createContext(0)

  module Provider = {
    let provider = React.Context.provider(context)

    @react.component
    let make = (~value, ~children) => {
      React.createElement(provider, {"value": value, "children": children})
    }
  }
}

//Dispatch Context Component
module DispatchContext = {
  let context = React.createContext((_action: action) => ())

  module Provider = {
    let provider = React.Context.provider(context)

    @react.component
    let make = (~value, ~children) => {
      React.createElement(provider, {"value": value, "children": children})
    }
  }
}

//Component
@react.component
let make = () => {
  let (state, dispatch) = React.useReducer((state, action) => {
    switch action {
    | Increment => state + 1
    | Decrement => state - 1
    }
  }, 0)
  <StateContext.Provider value=state>
    <DispatchContext.Provider value=dispatch>
      <div />
    </DispatchContext.Provider>
  </StateContext.Provider>
}

3 Likes

This code in the docs should work with @rescript/react. The issue was that it was documented without being released. This has the advantage not to generate any additional runtime.

1 Like

Here’s an example of using React Context:

Basically:

  1. create the context (line 15)
  2. declare the context provider (in this case it’s a module, but it can be declared as a file)
  3. wraps the component around the context provider (line 112)
  4. and use the context (line 44)

I am stuck using jsx3 due to the requirements of a dependency, and I have not been able to get past this error:

This has type: {"children": 'children, "value": 'value}
  Somewhere wanted: React.Context.props<int>

It even comes up when now opening your Playground. There has got to be a versioning incompatibility somewhere. Does anyone know? Thanks

The types should be equivalent given the definition of Context in rescript-react:

module Context = {
  type t<'context>

  type props<'context> = {
    value: 'context,
    children: element,
  }

  @get
  external provider: t<'context> => component<props<'context>> = "Provider"
}

SOLVED:
Ok, turns out this was just a matter of removing quotes. Here is the updated playground