Giter Club home page Giter Club logo

roact-rodux's Introduction

Roact-Rodux

A binding between Roact and Rodux.
 

Roact-Rodux is an ergonomic binding between Roact, a view library, and Rodux, a state management library.

It is the recommended way to use the two libraries together, and is similar in scope to react-redux, the equivalent library for React and Redux.

All documentation for Roact-Rodux is available on the official docs website. It includes:

Contributing

Contributions to Roact-Rodux are welcome! Details are available in the contributing guide.

License

Roact-Rodux is available under the Apache 2.0 license. See LICENSE for details.

roact-rodux's People

Contributors

amaranthinecodices avatar cliffchapmanrbx avatar conmur avatar conorgriffin37 avatar dougbankspersonal avatar hallenrbx avatar hmallen99 avatar jkelaty-rbx avatar lpghatguy avatar matthargett avatar ok-nick avatar oltrep avatar phalanxia avatar rgychiu avatar tonycuadra avatar zotethemighty avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

roact-rodux's Issues

Internal props being shoved in on Store change in Wally version

I was integrating multiplayer in my game and I came across this little bug:

Huh, that's funny. I was using an earlier version of RoactRodux and it was fine before switching to the Wally version...

If I print the props before a mapStateToProps call (line 97), like so, and add a print in my mapStateToProps:

I get this output:

After doing a little digging, it turns out that whenever the store updates, it shoves the Connection's raw props into mapStateToProps, instead of the Connection's innerProps.

If I pass in the innerProps like so:

It works like a charm:

Importing with Rotriever causes unit tests to fail

The Rotriever.toml file imports Roact, but the tests expect that Rodux is also imported.

The following tests fail with the same messages (TestService: Error during planning: Rodux is not a valid member of Folder) :

  • getStore.spec.lua
  • connect.spec.lua
  • StoreProvider.spec.lua

Please update the toml file to include Rodux.

No wally support

There is no wally support for this dependency, it's rather painful to manually install it across a few repos.

Documentation Request: Include a complete, annotated implementation example

As someone who struggled to understand this library after reading it from top to bottom, word-for-word, I would've really appreciated a complete, back-to-front example of Roact-Rodux implementation. I wrote this script below to help anyone in the future that wants to poke and prod at a working example :) Please consider putting this in the documentation after peer reviewing my comments for accuracy!

--[[
This is a complete example of Roact-Rodux that will create a 
TextButton that will increment its text when its .Activated event is fired!

1) Put Roact, Rodux and RoactRodux in ReplicatedStorage
2) Paste this script in a LocalScript
]]

--Init
local PlayerGui = game.Players.LocalPlayer.PlayerGui
local RS = game:GetService("ReplicatedStorage")
local Roact = require(RS:WaitForChild("Roact"))
local Rodux = require(RS:WaitForChild("Rodux"))
local RoactRodux = require(RS:WaitForChild("RoactRodux"))

--Rodux creates our store:
local function reducer(state, action)
	state = state or {
		value = 0,
	}
	if action.type == "increment" then
		return {
			value = state.value+1
		}
	end
	return state
end

local store = Rodux.Store.new(reducer)

--Roact creates our component:
local function MyComponent(props)
	--Roact.createElement(ClassName, {Class Properties}, [{Children}])
	return Roact.createElement("ScreenGui", {}, {
		Roact.createElement("TextButton", {
			Text = props.value,
			AnchorPoint = Vector2.new(0.5, 0.5),
			Position = UDim2.new(0.5, 0, 0.5, 0),
			Size = UDim2.new(0, 500, 0, 200),
			ZIndex = 0,

			[Roact.Event.Activated] = props.onClick,
		})
	})
end

--`RoactRodux.connect([mapStateToProps, [mapDispatchToProps]])` returns a function that we can pass our plain Roact component `MyComponent` created above as an argument, returning a new RoactRodux-connected component as our new `MyComponent` variable
MyComponent = RoactRodux.connect(
	function(state, props) 	--`mapStateToProps` accepts our store's state and returns props
		return {
			value = state.value,
		}
	end,
	function(dispatch) 		--`mapDispatchToProps` accepts a dispatch function and returns props
		print("RoactRodux mapDispatchToProps has run.")
		return {
			onClick = function()
				dispatch({
					type = "increment",
				})
			end,
		}
	end
)(MyComponent) --Here we are passing in our plain Roact component as an argument to RoactRodux.connect(...)
--`MyComponent` should now return a RoactRodux-connected component, which will update and re-render any time the store is changed.

--Here we will wrap a RoactRodux StoreProvider at the top of our Roact component tree. This will make our store readable to the elements & components below it via 
local app = Roact.createElement(RoactRodux.StoreProvider, {
	store = store,
}, {
	Main = Roact.createElement(MyComponent), --Due to the limitations of Roact, we can only have ONE(1) child under a StoreProvider element, so we have to call MyComponent to get the rest of the children. The Store will be passed to MyComponent as arguments.
})

--Roact will now put our app on the screen
Roact.mount(app, PlayerGui, "My Test App")

Support multiple children for StoreProvider

We should switch from Roact.oneChild to fragments in order to let consumers of Roact-Rodux have multiple children of the provider. We shipped the version we should target in Roact 1.2.

GitHub Actions

This repository had its Travis-CI integration disabled on accident. We should replace it with GitHub Actions instead of restoring it. This should just be a matter of adapting the Roact workflow for this repository.

Component to block updates below it temporarily

I want to hide some stuff without destroying it, but also block any Rodux updates until that piece is visible again.

Having a BlockStoreUpdates component would be helpful. It would essentially expose a signal that re-fires the changed event from the store, but only when it's enabled. Then, you can hide a part by updating its prop, as well as the visibility of the top-level container for the page.

Change connect(mapStoreToProps) to connect(mapStateToProps, mapDispatchToProps)

React-Redux's connect method accepts two functions, mapStateToProps and mapDispatchToProps. Roact-Rodux's only accepts one, mapStoreToProps.

Redux's API makes sense from both a performance and clarity perspective. You don't really want to recompute your mapDispatchToProps members very often, because you're usually generating function members. When dealing with pure components, that can generate a lot of diffing noise! On the clarity front, the two operations are fairly distinct ideas.

In React-Redux's case, they avoid passing the store around itself and instead usually pass around either one instance of its state or its dispatch method. I thought that was silly when I designed Roact-Rodux's API, so I kept them wrapped up in the Store object for all uses.

I'd like to change the Roact-Rodux API in one of two ways:

Connecting without a mapDispatchToProps function should inject dispatch to props to match react's behavior.

In essence, if mapDispatchToProps isn't specified, then the following function should be mapDispatchToProps instead:

function mapDispatchToProps(dispatch)
   return { dispatch = dispatch }
end

This would match the behavior that react has:

If you do not supply your own mapDispatchToProps function or object full of action creators, the default mapDispatchToProps implementation just injects dispatch into your component’s props.

Force connector to be pure

We should do a shallowEqual comparison after mapping props before we dispatch a change down the tree.

Roact-Rodux should've done this from the beginning.

Prop changes don't trigger mapStoreToProps

Consider a component using Roact-Rodux whose mapStoreToProps selector uses the little-known second parameter of props:

local function MyComponent(props)
    -- doesn't matter
end

MyComponent = RoactRodux.connect(function(store, props)
    -- Our selector just pulls whatever key you want out of the store
    return {
        value = store:GetState()[props.storeKey]
    }
end)

When props update, the predicate won't be invoked again, but the component will be able to observe that storeKey changed!

Use Roact propValidation

Instead of manually asserting about props in various places, we should use Roact's proper propValidation feature.

Update to new Context API

We're about to land a new context API that lines up with React's context API.

We should port Roact-Rodux to this new API in order to not hold up removal of the old context API.

I did a rough evaluation of this today and found:

  • UNSTABLE_getStore may be infeasible to replace, but is used internally at Roblox in a number of places.
  • StoreProvider is easy to change
  • connect will need significant non-trivial changes and is already difficult to grok

Update to new Roact APIs

We should migrate Roact-Rodux to use Roact.mount and Roact.unmount so that when users upgrade, they don't get warnings!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.