Skip to content

Commit

Permalink
React runtime warnings: add check for state initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
kLabz committed Aug 7, 2018
1 parent 1719431 commit ef0b0f1
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/lib/react/ReactComponentMacro.hx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class ReactComponentMacro {
react.ReactTypeMacro.ensureRenderOverride,
#end

#if (debug && react_render_warning)
// Note: react_render_warning should be deprecated
#if (debug && (react_render_warning || react_runtime_warnings))
react.ReactDebugMacro.buildComponent,
#end
];
Expand Down
82 changes: 82 additions & 0 deletions src/lib/react/ReactDebugMacro.hx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ReactDebugMacro
var pos = Context.currentPos();
var propsType:Null<ComplexType> = macro :Dynamic;
var stateType:Null<ComplexType> = macro :Dynamic;
var hasState = false;

switch (inClass.superClass)
{
Expand All @@ -26,13 +27,17 @@ class ReactDebugMacro

stateType = TypeTools.toComplexType(params[1]);
if (isEmpty(stateType)) stateType = null;
else hasState = true;

default:
}

if (!updateComponentUpdate(fields, inClass, propsType, stateType))
addComponentUpdate(fields, inClass, propsType, stateType);

if (hasState && !updateConstructor(fields, inClass, propsType, stateType))
addConstructor(fields, inClass, propsType, stateType);

return fields;
}

Expand All @@ -47,6 +52,83 @@ class ReactDebugMacro
};
}

static function updateConstructor(
fields:Array<Field>,
inClass:ClassType,
propsType:Null<ComplexType>,
stateType:Null<ComplexType>
) {
for (field in fields)
{
if (field.name == "new")
{
switch (field.kind) {
case FFun(f):
f.expr = macro {
${f.expr}
${exprConstructor(inClass)}
};

return true;
default:
}
}
}

return false;
}

static function addConstructor(
fields:Array<Field>,
inClass:ClassType,
propsType:Null<ComplexType>,
stateType:Null<ComplexType>
) {
var constructor = {
args: [
{
meta: [],
name: "props",
type: propsType == null ? macro :react.Empty : propsType,
opt: false,
value: null
}
],
ret: macro :Void,
expr: macro {
super(props);
${exprConstructor(inClass)};
}
}

fields.push({
name: 'new',
access: [APublic],
kind: FFun(constructor),
pos: inClass.pos
});
}

static function exprConstructor(inClass:ClassType)
{
return macro {
if (state == null) {
js.Browser.console.error(
'Warning: component ${inClass.name} is stateful but its '
+ '`state` is not initialized inside its constructor. '

+ 'Either add a `state = { ... }` statement to its constructor '
+ 'or define this component as a `ReactComponentOfProps` '
+ 'if it is only using `props`. '

+ 'If it is using neither `props` nor `state`, you might '
+ 'consider using `@:jsxStatic` to avoid unneeded lifecycle.'
// TODO: link to @:jsxStatic documentation when available
);
}
};
}

static function updateComponentUpdate(
fields:Array<Field>,
inClass:ClassType,
Expand Down

0 comments on commit ef0b0f1

Please sign in to comment.