Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

Commit

Permalink
Merge 15259c4 into a2f0047
Browse files Browse the repository at this point in the history
  • Loading branch information
LPGhatguy committed Feb 14, 2020
2 parents a2f0047 + 15259c4 commit dd82b64
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 35 deletions.
98 changes: 94 additions & 4 deletions docs/advanced/context.md
@@ -1,5 +1,95 @@
!!! warning
Context is an unstable feature that's being *significantly* revised. See [issue #4](https://github.com/Roblox/roact/issues/4) for current progress.
!!! danger "Unreleased API"
This API is not yet available in a stable Roact release.

!!! info
This section is incomplete. It's possible that the context API will change before the existing API is ever documented.
It may be available from a recent pre-release or Roact's master branch.

[TOC]

Roact supports a feature called context which enables passing values down the tree without having to pass them through props. Roact's Context API is based on [React's Context API](https://reactjs.org/docs/context.html).

Context is commonly used to implement features like dependency injection, dynamic theming, and scoped state storage.

## Basic Usage
```lua
local ThemeContext = Roact.createContext(defaultValue)
```

Context objects contain two components, `Consumer` and `Provider`.

The `Consumer` component accepts a `render` function as its only prop, which is used to render its children. It's passed one argument, which is the context value from the nearest matching `Provider` ancestor.

If there is no `Provider` ancestor, then `defaultValue` will be passed instead.

```lua
local function ThemedButton(props)
return Roact.createElement(ThemeContext.Consumer, {
render = function(theme)
return Roact.createElement("TextButton", {
Size = UDim2.new(0, 100, 0, 100),
Text = "Click Me!",
TextColor3 = theme.foreground,
BackgroundColor3 = theme.background,
})
end
})
end
```

The `Provider` component accepts a `value` prop as well as children. Any of its descendants will have access to the value provided to it by using the `Consumer` component like above.

Whenever the `Provider` receives a new `value` prop in an update, any attached `Consumer` components will re-render with the new value. This value could be externally controlled, or could be controlled by state in a component wrapping `Provider`:

```lua
local ThemeController = Roact.Component:extend("ThemeController")

function ThemeController:init()
self:setState({
theme = {
foreground = Color3.new(1, 1, 1),
background = Color3.new(0, 0, 0),
}
})
end

function ThemeController:render()
return Roact.createElement(ThemeContext.Provider, {
value = self.state.theme,
}, self.props[Roact.Children])
end
```

## Legacy Context
Roact also has a deprecated version of context that pre-dates the stable context API. It will be removed in a future release, but is currently maintained for backwards-compatibility.

Legacy context values cannot be updated dynamically. It is up to the context user to create their own mechanism for updates, probably using a wrapper component and `setState`.

To use it, add new entries to `self._context` in `Component:init()` to create a provider:

```lua
local Provider = Roact.Component:extend("FooProvider")

-- Using a unique non-string key is recommended to avoid collisions.
local FooKey = {}

function Provider:init()
self._context[FooKey] = {
value = 5,
}
end
```

...and read from that same value in `Component:init()` in your consumer component:

```lua
local Consumer = Roact.Component:extend("FooConsumer")

function Consumer:init()
self.foo = self._context[FooKey]
end

function Consumer:render()
return Roact.createElement("TextLabel", {
Text = "Foo: " .. self.foo.value,
})
end
```
48 changes: 44 additions & 4 deletions docs/api-reference.md
Expand Up @@ -17,7 +17,8 @@ The `children` argument is shorthand for adding a `Roact.Children` key to `props
---

### Roact.createFragment
<div class="api-addition">Added in 1.0.0</div>

!!! success "Added in Roact 1.0.0"

```
Roact.createFragment(elements) -> RoactFragment
Expand Down Expand Up @@ -84,7 +85,8 @@ If `children` is `nil` or contains no children, `oneChild` will return `nil`.
---

### Roact.createBinding
<div class="api-addition">Added in 1.0.0</div>

!!! success "Added in Roact 1.0.0"

```
Roact.createBinding(initialValue) -> Binding, updateFunction
Expand Down Expand Up @@ -118,7 +120,8 @@ Returns a new binding that maps the existing binding's value to something else.
---

### Roact.joinBindings
<div class="api-addition">Added in 1.1.0</div>

!!! success "Added in Roact 1.1.0"

```
Roact.joinBindings(bindings) -> Binding
Expand Down Expand Up @@ -176,6 +179,39 @@ Creates a new reference object that can be used with [Roact.Ref](#roactref).

---

### Roact.createContext

!!! danger "Unreleased API"
This API is not yet available in a stable Roact release.

It may be available from a recent pre-release or Roact's master branch.

```
Roact.createContext(defaultValue: any) -> { Provider, Consumer }
```

Creates a new context provider and consumer. For a usage guide, see [Advanced Concepts: Context](/advanced/context).

`defaultValue` is given to consumers if they have no `Provider` ancestors. It is up to consumers of Roact's context API to turn this case into an error if it is an invalid state.

`Provider` and `Consumer` are both Roact components.

#### `Provider`
`Provider` accepts the following props:

* `value`: The value to put into the tree for this context value.
* If the `Provider` is updated with a new `value`, any matching `Consumer` components will be re-rendered with the new value.
* `[Children]`: Any number of children to render underneath this provider.
* Descendants of this component can receiver the provided context value by using `Consumer`.

#### `Consumer`
`Consumer` accepts just one prop:

* `render(value) -> RoactElement | nil`: A function that will be invoked to render any children.
* `render` will be called every time `Consumer` is rendered.

---

### Roact.setGlobalConfig
```
Roact.setGlobalConfig(configValues: Dictionary<string, bool>) -> void
Expand Down Expand Up @@ -503,7 +539,8 @@ By default, components are re-rendered any time a parent component updates, or w
---

### validateProps
<div class="api-addition">Added in 1.0.0</div>

!!! success "Added in Roact 1.0.0"

```
static validateProps(props) -> (false, message: string) | true
Expand All @@ -523,6 +560,9 @@ Roact.setGlobalConfig({

See [setGlobalConfig](#roactsetglobalconfig) for more details.

!!! note
`validateProps` is a *static* lifecycle method. It does not have access to `self`, and must be a pure function.

!!! warning
Depending on the implementation, `validateProps` can impact performance. Recommended practice is to enable prop validation during development and leave it off in production environments.

Expand Down
29 changes: 2 additions & 27 deletions docs/extra.css
@@ -1,28 +1,3 @@
.api-addition {
display: flex;
align-content: center;
align-items: center;
vertical-align: middle;

padding: 0.4rem 0.6rem 0.4rem 0.5rem;
background-color: #efffec;
border-left: 0.2rem solid green;
border-radius: 0.1rem;

line-height: 1;
font-size: 0.64rem;
font-weight: bold;

box-shadow:
0 2px 2px 0 rgba(0,0,0,.14),
0 1px 5px 0 rgba(0,0,0,.12),
0 3px 1px -2px rgba(0,0,0,.2);
}

.api-addition::before {
flex: 0 0 1.5rem;
content: "+";
color: green;
font-size: 1.5em;
text-align: center;
.md-typeset hr {
border-bottom: 2px solid rgba(0, 0, 0, 0.15);
}

0 comments on commit dd82b64

Please sign in to comment.