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

Pass props and state through render #242

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Roact Changelog

## Unreleased Changes
* `render` now passes the incoming props and state ([#199](https://github.com/Roblox/roact/issues/199))

## [1.2.0](https://github.com/Roblox/roact/releases/tag/v1.2.0) (September 6th, 2019)
* Fixed a bug where derived state was lost when assigning directly to state in init ([#232](https://github.com/Roblox/roact/pull/232/))
Expand Down
18 changes: 14 additions & 4 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,20 +400,30 @@ end

### render
```
render() -> Element | nil
render(props, state) -> Element | nil
```

`render` describes what a component should display at the current instant in time.

You can access the props and state of the component either through `self.props` and `self.state` respectively or through the arguments passed.

```lua
function MyComponent:render(props, state)
return Roact.createElement("TextLabel", {
Text = "Hello, " .. props.name .. "!"
})
end
```

!!! info
Roact assumes that `render` act likes a pure function: the result of `render` must depend only on `props` and `state`, and it must not have side-effects.

```lua
function MyComponent:render()
function MyComponent:render(props, state)
-- This is okay:
return Roact.createElement("TextLabel", {
Text = self.props.text,
Position = self.state.position
Text = props.text,
Position = state.position
})

-- Ack! Depending on values outside props/state is not allowed!
Expand Down
6 changes: 2 additions & 4 deletions src/Component.lua
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,6 @@ end
Returns a snapshot of this component given the current props and state. Must
be overridden by consumers of Roact and should be a pure function with
regards to props and state.

TODO (#199): Accept props and state as arguments.
]]
function Component:render()
local internalData = self[InternalData]
Expand Down Expand Up @@ -287,7 +285,7 @@ function Component:__mount(reconciler, virtualNode)
virtualNode.context = instance._context

internalData.lifecyclePhase = ComponentLifecyclePhase.Render
local renderResult = instance:render()
local renderResult = instance:render(props, instance.state)

internalData.lifecyclePhase = ComponentLifecyclePhase.ReconcileChildren
reconciler.updateVirtualNodeWithRenderResult(virtualNode, hostParent, renderResult)
Expand Down Expand Up @@ -450,7 +448,7 @@ function Component:__resolveUpdate(incomingProps, incomingState)
self.props = incomingProps
self.state = incomingState

local renderResult = virtualNode.instance:render()
local renderResult = virtualNode.instance:render(incomingProps, incomingState)

internalData.lifecyclePhase = ComponentLifecyclePhase.ReconcileChildren
reconciler.updateVirtualNodeWithRenderResult(virtualNode, virtualNode.hostParent, renderResult)
Expand Down
15 changes: 9 additions & 6 deletions src/Component.spec/render.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ return function()

expect(renderSpy.callCount).to.equal(1)

local renderArguments = renderSpy:captureValues("self")
local renderArguments = renderSpy:captureValues("self", "props", "state")

expect(Type.of(renderArguments.self)).to.equal(Type.StatefulComponentInstance)
assertDeepEqual(capturedProps, {})
Expand All @@ -57,7 +57,10 @@ return function()

local capturedProps
local capturedState
local renderSpy = createSpy(function(self)
local renderSpy = createSpy(function(self, props, state)
expect(props).to.equal(self.props)
expect(state).to.equal(self.state)

capturedProps = self.props
capturedState = self.state
end)
Expand All @@ -74,7 +77,7 @@ return function()

expect(renderSpy.callCount).to.equal(1)

local firstRenderArguments = renderSpy:captureValues("self")
local firstRenderArguments = renderSpy:captureValues("self", "props", "state")
local firstProps = capturedProps
local firstState = capturedState

Expand All @@ -91,7 +94,7 @@ return function()

expect(renderSpy.callCount).to.equal(2)

local secondRenderArguments = renderSpy:captureValues("self")
local secondRenderArguments = renderSpy:captureValues("self", "props", "state")
local secondProps = capturedProps
local secondState = capturedState

Expand Down Expand Up @@ -127,7 +130,7 @@ return function()

expect(renderSpy.callCount).to.equal(1)

local firstRenderArguments = renderSpy:captureValues("self")
local firstRenderArguments = renderSpy:captureValues("self", "props", "state")
local firstProps = capturedProps
local firstState = capturedState

Expand All @@ -137,7 +140,7 @@ return function()

expect(renderSpy.callCount).to.equal(2)

local renderArguments = renderSpy:captureValues("self")
local renderArguments = renderSpy:captureValues("self", "props", "state")

expect(Type.of(renderArguments.self)).to.equal(Type.StatefulComponentInstance)
expect(capturedProps).to.equal(firstProps)
Expand Down