@@ -2,6 +2,18 @@ local RunService = game:GetService("RunService")
22
33local Signal = require (script .Parent .Signal )
44local NoYield = require (script .Parent .NoYield )
5+ local inspect = require (script .Parent .inspect ).inspect
6+
7+ local defaultErrorReporter = {
8+ reportErrorDeferred = function (self , message , stacktrace )
9+ print (message )
10+ print (stacktrace )
11+ end ,
12+ reportErrorImmediately = function (self , message , stacktrace )
13+ print (message )
14+ print (stacktrace )
15+ end
16+ }
517
618local Store = {}
719
@@ -23,22 +35,41 @@ Store.__index = Store
2335 Reducers do not mutate the state object, so the original state is still
2436 valid.
2537]]
26- function Store .new (reducer , initialState , middlewares )
38+ function Store .new (reducer , initialState , middlewares , errorReporter )
2739 assert (typeof (reducer ) == " function" , " Bad argument #1 to Store.new, expected function." )
2840 assert (middlewares == nil or typeof (middlewares ) == " table" , " Bad argument #3 to Store.new, expected nil or table." )
41+ if middlewares ~= nil then
42+ for i = 1 , # middlewares , 1 do
43+ assert (
44+ typeof (middlewares [i ]) == " function" ,
45+ (" Expected the middleware ('%s') at index %d to be a function." ):format (tostring (middlewares [i ]), i )
46+ )
47+ end
48+ end
2949
3050 local self = {}
3151
52+ self ._errorReporter = errorReporter or defaultErrorReporter
53+ self ._isDispatching = false
3254 self ._reducer = reducer
33- self . _state = reducer ( initialState , {
55+ local initAction = {
3456 type = " @@INIT" ,
35- })
57+ }
58+ self ._lastAction = initAction
59+ local ok , result = pcall (function ()
60+ self ._state = reducer (initialState , initAction )
61+ end )
62+ if not ok then
63+ local message = (" Caught error with init action of reducer (%s): %s" ):format (tostring (reducer ), tostring (result ))
64+ errorReporter :reportErrorImmediately (message , debug.traceback ())
65+ self ._state = initialState
66+ end
3667 self ._lastState = self ._state
3768
3869 self ._mutatedSinceFlush = false
3970 self ._connections = {}
4071
41- self .changed = Signal .new ()
72+ self .changed = Signal .new (self )
4273
4374 setmetatable (self , Store )
4475
@@ -58,7 +89,7 @@ function Store.new(reducer, initialState, middlewares)
5889 dispatch = middleware (dispatch , self )
5990 end
6091
61- self .dispatch = function (self , ...)
92+ self .dispatch = function (_self , ...)
6293 return dispatch (... )
6394 end
6495 end
70101 Get the current state of the Store. Do not mutate this!
71102]]
72103function Store :getState ()
104+ if self ._isDispatching then
105+ error ((" You may not call store:getState() while the reducer is executing. " ..
106+ " The reducer (%s) has already received the state as an argument. " ..
107+ " Pass it down from the top reducer instead of reading it from the store." ):format (tostring (self ._reducer )))
108+ end
109+
73110 return self ._state
74111end
75112
113+ function Store :_reportReducerError (failedAction , error_ , traceback )
114+ local message = (" Caught error when running action (%s) " ..
115+ " through reducer (%s): \n %s \n " ..
116+ " previous action type was: %s"
117+ ):format (
118+ tostring (failedAction ),
119+ tostring (self ._reducer ),
120+ tostring (error_ ),
121+ inspect (self ._lastAction )
122+ )
123+
124+ self ._errorReporter :reportErrorImmediately (message , traceback )
125+ end
126+
76127--[[
77128 Dispatch an action to the store. This allows the store's reducer to mutate
78129 the state of the application by creating a new copy of the state.
81132 changes, but not necessarily on every Dispatch.
82133]]
83134function Store :dispatch (action )
84- if typeof (action ) == " table" then
85- if action .type == nil then
86- error (" action does not have a type field" , 2 )
87- end
135+ if typeof (action ) ~= " table" then
136+ error ((" Actions must be tables. " ..
137+ " Use custom middleware for %q actions." ):format (typeof (action )),
138+ 2
139+ )
140+ end
88141
142+ if action .type == nil then
143+ error (" Actions may not have an undefined 'type' property. " ..
144+ " Have you misspelled a constant? \n " ..
145+ inspect (action ), 2 )
146+ end
147+
148+ if self ._isDispatching then
149+ error (" Reducers may not dispatch actions." )
150+ end
151+
152+ local ok , result = pcall (function ()
153+ self ._isDispatching = true
89154 self ._state = self ._reducer (self ._state , action )
90155 self ._mutatedSinceFlush = true
91- else
92- error ((" actions of type %q are not permitted" ):format (typeof (action )), 2 )
156+ end )
157+
158+ self ._isDispatching = false
159+
160+ if not ok then
161+ self :_reportReducerError (
162+ action ,
163+ result ,
164+ debug.traceback ()
165+ )
93166 end
167+ self ._lastAction = action
94168end
95169
96170--[[
0 commit comments