How to define a binding that accepts multiple types in the function signature using reason-react? - ffi

When defining a reason-react binding and I want to know how I can determine a binding that accepts multiple types. For example, I have an argument ~value that should accept: string, number, array(string) or array(number). At the moment I am using option('a) but I do not think this is the cleanest approach as I would prefer to define the type explicitly. How can this be done? I have looked at bs.unwrap but I am unsure how to combine external syntax into a function signature.
module Select = {
[#bs.module "material-ui/Select"] external reactClass : ReasonReact.reactClass = "default";
let make =
(
...
~menuProps: option(Js.t({..}))=?,
~value: option('a), /* Should be type to string, number, Array of string and Array of number */
~style: option(ReactDOMRe.style)=?,
...
children
) =>
ReasonReact.wrapJsForReason(
~reactClass,
~props=
Js.Nullable.(
{
...
"value": from_opt(value),
"style": from_opt(style)
}
),
children
);
};
As a side question, as number type is not defined in reason would my binding also have to map float and integer into numbers?

This is possible by using the following (inspired by https://github.com/astrada/reason-react-toolbox/).
type jsUnsafe;
external toJsUnsafe : 'a => jsUnsafe = "%identity";
let unwrapValue =
(r: [< | `Int(int) | `IntArray(array(int)) | `String(string) | `StringArray(array(string))]) =>
switch r {
| `String(s) => toJsUnsafe(s)
| `Int(i) => toJsUnsafe(i)
| `StringArray(a) => toJsUnsafe(a)
| `IntArray(a) => toJsUnsafe(a)
};
let optionMap = (fn, option) =>
switch option {
| Some(value) => Some(fn(value))
| None => None
};
module Select = {
[#bs.module "material-ui/Select"] external reactClass : ReasonReact.reactClass = "default";
let make =
(
...
~menuProps: option(Js.t({..}))=?,
~value:
option(
[ | `Int(int) | `IntArray(array(int)) | `String(string) | `StringArray(array(string))]
)=?,
~style: option(ReactDOMRe.style)=?,
...
children
) =>
ReasonReact.wrapJsForReason(
~reactClass,
~props=
Js.Nullable.(
{
...
"value": from_opt(optionMap(unwrapValue, value)),
"style": from_opt(style)
}
),
children
);
};
This can be used in the following way;
<Select value=(`IntArray([|10, 20|])) />
<Select value=(`Int(10)) />
I copied toJsUnsafe from reason-react-toolbox, so I'm not entirely sure exactly what it does, I will update my answer when I find out.
The unwrapValue function takes a value which can be one of the types listed and converts it to jsUnsafe.
The type for unwrapValue allows for any of variants listed, but also allows a subset of those, for example. (It's the < before the variants that enable this).
let option = (value: option([ | `String(string) | `Int(int)])) =>
Js.Nullable.from_opt(option_map(unwrapValue, value));

Just to add to #InsidersByte's answer, since this problem isn't reason-react-specific and can be generalized:
module Value = {
type t;
external int : int => t = "%identity";
external intArray : array(int) => t = "%identity";
external string : string => t = "%identity";
external stringArray : array(string) => t = "%identity";
};
let values : list(Value.t) = [
Value.int(4),
Value.stringArray([|"foo", "bar"|])
];
This solution is also self-contained inside the Value module, and incurs no overhead compared to the JavaScript equivalent since "%identity" externals are no-ops that are optimized away.

Related

Solve the typescript - "No matches overload call" with ref

I am going to solve the typescript correspondent.
Here is the code and the error what I got.
const navRef = useRef<null | HTMLElement>(null);
const setFocusables = () => {
let navCurrent = navRef.current || null;
menuFocusables = [
buttonRef.current,
...Array.from(navCurrent?.querySelectorAll('a')),
];
firstFocusableEl = menuFocusables[0];
lastFocusableEl = menuFocusables[menuFocusables.length - 1];
};
Here is the error what I got it now.
let navCurrent: HTMLElement | null No overload matches this call.
Overload 1 of 4, '(iterable: Iterable |
ArrayLike): HTMLAnchorElement[]', gave the
following error. Argument of type 'NodeListOf
| undefined' is not assignable to parameter of type
'Iterable | ArrayLike'.
Type 'undefined' is not assignable to type
'Iterable | ArrayLike'.
Overload 2 of 4, '(arrayLike: ArrayLike):
HTMLAnchorElement[]', gave the following error. Argument of type
'NodeListOf | undefined' is not assignable to
parameter of type 'ArrayLike'. Type
'undefined' is not assignable to type
'ArrayLike'.ts(2769)
Please give me solution.
Thank you in advance.
Hi.
I hope to solve the above problem.
Thanks
You need to handle when navCurrent is null, so it should be like:
const navRef = useRef<null | HTMLElement>(null);
const setFocusables = () => {
let navCurrent = navRef.current || null;
menuFocusables = [
buttonRef.current,
// updated this
...Array.from(navCurrent?.querySelectorAll('a') ?? []),
];
firstFocusableEl = menuFocusables[0];
lastFocusableEl = menuFocusables[menuFocusables.length - 1];
};

Get the specific data (seatType: & seatTypeId:) from an Array React

I have an Array that has the following data & I want to get the specific attributes to variables which I need to pass through URL parameters.
Array
MovieSeatList:[
adultPrice: 350
childAvailable: true
childPrice: 280
seatRows: [Array(5)]
seatType: "ODC"
seatTypeId: 536
]
code:
render() {
const {MovieSeatList, dataLoaded} = this.state;
console.log(MovieSeatList)
// MoviedatatoPlan = MovieSeatList
console.log(MovieSeatList)
console.log(movieData)
}
It would be better if I can get the following attributes to variables that will be passed in URL parameters.
seatType:
seatTypeId:
let seatType = MovieSeatList.map(a => a.seatType);
let seatTypeId = MovieSeatList.map(b => b.seatTypeId);

Need help to make [create-react-app] to use Aync - Await (transform-async-to-generator)!

I am new to [create-react-app] and I would like to find out how can I add : ["transform-async-to-generator"] to this build process? In regular cases I would add it in .babelrc, but does not look to work* with [create-react-app].
*By "does not look to work" - I see the following error.
Syntax error: ../web/src/App.js: Unexpected token, expected ( (17:13)
15 | }
16 |
> 17 | test = async () => {
| ^
18 | let x = await this.resolveAfter2Seconds();
19 | try{}
20 | catch(exception){
And is any way to extend [create-react-app], without modifying the package itself?
Thanks!
The problem is not with async functions. You should rewrite your code the next way:
// ...
test = async () => {
let x = await this.resolveAfter2Seconds();
// ...
}
or
// ...
async test(){
let x = await this.resolveAfter2Seconds();
// ...
}
You should add 2 plugins to babel: babel-plugin-transform-async-to-generator and babel-plugin-syntax-async-functions

How to write/use a anorm Extractor like rowToStringSequence Column[Seq[String]]

i wrote this row converter.
implicit def rowToStringSequence: Column[Seq[String]] = Column.nonNull { (value, meta) =>
val MetaDataItem(qualified, nullable, clazz) = meta
value match {
case data: Seq[String] => Right(data)
case _ => Left(TypeDoesNotMatch(
"Cannot convert " + value + ":" + value.asInstanceOf[AnyRef].getClass +
" to String Array for column " + qualified))
}
}
Unfortunately, I do not know how to use it within a case class.
For instance:
case class profile ( eyeColor: Seq[String] )
The profile companion object:
object Profile{
val profile= {
get[Seq[String]]("eyeColor") map {
case
eyeColor => Profile(eyeColor)
}
}
}
The compilation error message is:
could not find implicit value for parameter extractor: anorm.Column[Seq[String]]
I need a hint.
Thank you!!
anorm.Column is made to convert JDBC data to desired Scala type. So first question is which kind of JDBC you want to convert as Seq[String] (not being by itself a JDBC type).

parse JSON data with different structures

I have a problem with accessing a certain property(hlink) in JSON code. This is because the structure of the JSON output is not always the same ,and as a result I get following error: "Cannot use object of type stdClass as array in...". Can someone help me to solve this problem?
JSON output 1 (Array)
Array (
[0] => stdClass Object (
[hlink] => http://www.rock-zottegem.be/
[main] => true
[mediatype] => webresource )
[1] => stdClass Object (
[copyright] => Rock Zottegem
[creationdate] => 20/03/2013 14:35:57
[filename] => b014933c-fdfd-4d93-939b-ac7adf3a20a3.jpg
[filetype] => jpeg
[hlink] => http://media.uitdatabank.be/20130320/b014933c-fdfd-4d93-939b-ac7adf3a20a3.jpg
)
JSON output 2
stdClass Object (
[copyright] => Beschrijving niet beschikbaar
[creationdate] => 24/04/2013 19:22:47
[filename] => Cinematek_F14281_1.jpg
[filetype] => jpeg
[hlink] => http://media.uitdatabank.be/20130424/Cinematek_F14281_1.jpg
[main] => true
[mediatype] => photo
)
And this is my code:
try {
if (!empty($img[1]->hlink)){
echo "<img src=" . $img[1]->hlink . "?maxheight=300></img>";
}
}
catch (Exception $e){
$e->getMessage();
}
Assuming this is PHP, and you know that the JSON always contains either an object or an array of objects, the problem boils down to detecting which you've received.
Try something like:
if (is_array($img)) {
$hlink = $img[0]->hlink;
} else {
$hlink = $img->hlink;
}
This isn't directly answering your question, but it should give you the means to investigate the problems you are having.
Code Sample
var obj1 = [ new Date(), new Date() ];
var obj2 = new Date();
obj3 = "bad";
function whatIsIt(obj) {
if (Array.isArray(obj)) {
console.log("obj has " + obj.length + " elements");
} else if (obj instanceof Date) {
console.log(obj.getTime());
} else {
console.warn("unexpected object of type " + typeof obj);
}
}
// Use objects
whatIsIt(obj1);
whatIsIt(obj2);
whatIsIt(obj3);
// Serialize objects as strings
var obj1JSON = JSON.stringify(obj1);
// Notice how the Date object gets stringified to a string
// which no longer parses back to a Date object.
// This is because the Date object can be fully represented as a sting.
var obj2JSON = JSON.stringify(obj2);
var obj3JSON = JSON.stringify(obj3);
// Parse strings back, possibly into JS objects
whatIsIt(JSON.parse(obj1JSON));
// This one became a string above and stays one
// unless you construct a new Date from the string.
whatIsIt(JSON.parse(obj2JSON));
whatIsIt(JSON.parse(obj3JSON));
Output
obj has 2 elements JsonExample:6
1369955261466 JsonExample:8
unexpected object of type string JsonExample:10
obj has 2 elements JsonExample:6
unexpected object of type string JsonExample:10
unexpected object of type string JsonExample:10
undefined

Resources