order | title |
---|---|
0 |
React "next" |
Branch #next
of my haxe-react fork aims to move haxe-react forward to 2.0.0+
with an increased freedom to break things until I get them right.
This version of haxe-react can be considered unstable (even though it is currently being used in production) due to these huge changes it can go through. You may want to lock your dependencies to the latest commit of the branch instead of the branch itself, if you are not willing to update your code every now and then. I am available in haxe-react gitter if you need help.
Haxe 4 (preview 4 atm) is used as the main target version in this fork, but Haxe 3.4.7 should still be supported.
Based off back2dos's PR #95, this
branch uses tink_hxx
to handle jsx.
The change of parser implies some little syntax changes:
prop=$42
and such are no longer allowed, useprop=${42}
orprop={42}
prop=${true}
can now be expressed as simplyprop
- Props are type-checked against the component's
TProps
- You cannot pass props not recognized by the target component
- Haxe 4 inline markup is supported and can replace the jsx string
- Hxx's control structures
You can use ${/* comments */}
/ {/* comments */}
in jsx to add comments
that won't be included in generated javascript.
You can also comment props with //
inside the opening tag:
<input
type="text"
// onClick={someFunction}
/>
6e8fe8d
Allow String variables as jsx node
The new parser will resolve String
variables for node names:
var Node = isTitle ? 'h2' : 'p';
return jsx('<$Node>${props.children}</$Node>');
Warning: it only works for variable names starting with an uppercase letter.
d173de0
Fix error position when using invalid nodes in jsx
Using an invalid node inside jsx, such as <$UnknownComponent />
, resulted in
an error inside haxe.macro.MacroStringTools
.
This fix ensures that the position points to "UnknownComponent" inside the jsx string.
578c55d
Disallow invalid values inside jsx when a fragment is expected
For example, the following used to compile:
jsx('<div>${function() return 42}</div>');
But resulted in a runtime error:
Warning: Functions are not valid as a React child. This may happen if you
return a Component instead of <Component /> from render. Or maybe you meant
to call this function rather than return it.
Or, for objects: jsx('<div>${{test: 42}}</div>');
resulted in:
Uncaught Error: Objects are not valid as a React child (found: object with
keys {test}). If you meant to render a collection of children,
use an array instead.
Now we get a compilation error (see below for ReactFragment
):
src/Index.hx:31: characters 7-17 : { test : Int } should be react.ReactFragment
src/Index.hx:31: characters 7-17 : For function argument 'children'
d06bc25
... unless in a component allowing another type for its children
prop
Components can handle their children
prop any way they want, and so this prop
may be of any type unless it is actually used as a react node.
This commit does two things:
- Partially revert above commit
578c55d
for allowing other values - Unifies components' children with their
children
prop if any, or withReactFragment
if none is defined
This allows things like that:
jsx('<$MyComponent>${() -> 42}</$MyComponent>');
// ...
typedef Props = {
var children:Void->Int;
}
class MyComponent extends ReactComponentOfProps<Props> {
override public function render() {
return jsx('<p>The answer is ${props.children()}</p>');
}
}
While still disallowing above examples.
425cb6c
Ensure individual prop typing, allowing abstract props to do their magic
Makes sure each prop resolves to its type, with a (prop :TypeOfProp)
.
This will trigger abstracts @:from
/ @:to
which may be needed in some cases
to do their magic.
Tries to extract the list of needed props and adds a compilation warning when some of them are not passed in a jsx "call".
Limitations:
- If you use the spread operator on the props of a component, this test is not executed (it becomes hard and even sometimes impossible to know what props are passed through the spread).
affd6e4
Jsx: handle data- and aria- props
aria-
props are type-checked against their specification, and
enums are provided where necessary (see react.jsx.AriaAttributes
). They are
enabled by default for both html elements and react components. You can disable
aria-
props entirely with -D react_jsx_no_aria
, or only disable it for react
components with -D react_jsx_no_aria_for_components
.
data-
props are enabled by default for react components (all unknown props are
already accepted for html elements), with a type of Dynamic
. They can be
disabled with -D react_jsx_no_data_for_components
.
Add support for string interpolation in attributes:
<Comp foo="hello ${target}" />
.
Cherry-picked and improved PR #108, which removed the legacy TRefs
from ReactComponent
.
ReactComponentOf<TProps, TState>
(orReactComponentOfPropsAndState<TProps, TState>
)ReactComponentOfProps<TProps>
ReactComponentOfState<TState>
- And still
ReactComponent
which has untyped props and state (Dynamic
)
This is actually a big change, since ReactComponentOfProps
and
ReactComponentOfState
use react.Empty
type as TState
(resp. TProps
).
react.Empty
is an empty typedef, disabling state access/update on
ReactComponentOfProps
, and props access in ReactComponentOfState
.
React does not support non-objects (and also does not support arrays) as props
or state for your components. Type parameters constraints have been added to
ensure at compile time that you don't use unsupported types for TProps
and/or
TState
.
ReactFragment
(in react.ReactComponent
module) tries to be closer to react
in describing a valid element. It replaces ReactElement
in most API, allowing
them to use other types allowed by react.
ReactSingleFragment
Array<ReactFragment>
ReactElement
String
Float
(orInt
)Bool
This type can be used when you expect a single element and not a collection of elements.
react.React
:
public static function createElement(type:CreateElementType, ?attrs:Dynamic, children:haxe.extern.Rest<Dynamic>):ReactElement;
public static function isValidElement(object:ReactFragment):Bool;
public static function forwardRef<TProps, TRef>(render:TProps->ReactRef<TRef>->ReactFragment):ReactType;
react.React.Children
:
function map(children:Dynamic, fn:ReactFragment->ReactFragment):Null<Array<ReactFragment>>;
function foreach(children:Dynamic, fn:ReactFragment->Void):Void;
function count(children:ReactFragment):Int;
function only(children:ReactFragment):ReactSingleFragment;
function toArray(children:ReactFragment):Array<ReactFragment>;
react.ReactDOM
:
public static function render(element:ReactFragment, container:Element, ?callback:Void -> Void):ReactFragment;
public static function hydrate(element:ReactFragment, container:Element, ?callback:Void -> Void):ReactFragment;
public static function createPortal(child:ReactFragment, container:Element):ReactFragment;
react.ReactType
replaces CreateElementType
and allows:
String
Void->ReactFragment
TProps->ReactFragment
Class<ReactComponent>
@:jsxStatic
components
There is also ReactTypeOf<TProps>
, for cases when you want a component
accepting some specific props.
CreateElementType
, still in the react.React
module, is now deprecated
but still available as a proxy to ReactType
.
ReactType
was first implemented as ReactNode
, but has been renamed in
1.103.0
to avoid confusion with ReactJS names. ReactNode
is temporarily
available as a proxy to ReactType
, but will be removed before 2.0.0
.
@:jsxStatic
has been better integrated with ReactType
, making it usable
outside jsx like any other component.
See Static components.
@:wrap
has been improved to support strict typing in jsx, and since it is
using @:jsxStatic
under the hood it also benefits from the fixed usage outside
jsx via ReactType
abstract.
See Wrapping your components in HOCs for more information about wrapping components in HOC.
TODO: Documentation for @:pureComponent
+ PureComponent
extern, differences
between macro implementation and extern.
98233c3
Add warning if ReactComponent's render has no override
Adds a compile-time check for an override of the render
function in your
components. This helps catching following runtime warning sooner:
Warning: Index(...): No `render` method found on the returned component
instance: you may have forgotten to define `render`.
Catching it at compile-time also ensures it does not happen to a component only visible for a few specific application state.
You can disable this with the -D react_ignore_empty_render
compilation flag,
or for a specific component by adding @:ignoreEmptyRender
meta to it.
ef0b0f1
React runtime warnings: add check for state initialization
React runtime warnings, disabled by default, can be enabled with the
-D react_runtime_warnings
compilation flag (only when -debug
is enabled).
They were previously enabled with -D react_render_warning
, and only added the
warning about avoidable re-renders. Note that this warning can have false
positive due to the legacy context API (react-router for example). You can
disable it for a specific component by adding @:ignoreRenderWarning
meta to
this component (a7860c6
).
A new warning has been added: if a component having a state does not have a
constructor or has one but doesn't initialize its state in it, you will get a
runtime error warning you about it (instead of an unclear error later when
accessing state
).
These warnings are now more accurate since the strict props/state types have
been added to ReactComponentOf
typedefs. Compatibility has been handled
mainly in 1719431
and 241a13b
.
b3286e1
Added access and types for React Shared Internals
Access react shared internals via react.React._internals
during renders to
get debug data (stack, timings, etc.). See react.ReactSharedInternals
. Note:
this has been based on current react version (16.4.2), and may not be fully
compatible with other versions.
[WIP] Random examples of what is available there:
React._internals.ReactDebugCurrentFrame.getCurrentStack()
will list all parent nodes of current element, up to the root node of the application
TODO
Generate PropTypes for more runtime checks on props
There are already many checks at compile time to ensure you are not doing obvious mistakes. However, sometimes the compiler cannot see what is really happening, and only a runtime check can really tell you what went wrong.
prop-types
can check a number of things at runtime, roughly
like the haxe compiler would do at compile time. But writing both TProps
and
propTypes
for your components would be too much.
This feature, enabled when you compile with both -debug
and the compilation
flag -D react_generate_proptypes
, will generate propTypes for your components,
using their TProps
as a reference.
This does not overwrite manually written propTypes, and ignores completely extern classes (most react libraries will provide propTypes anyway, probably more accurate than these).