Привязка к компоненту более высокого порядка из aws-ampify

То, что ищет bucklescript, удовлетворяет ошибке Functions are not valid as a React child., возникающей в следующем примере.

Имею эту привязку к withAuthenticator от aws-amplify-react.

[@bs.deriving abstract]
type props = {
  [@bs.as "Comp"]
  comp: React.element,
  [@bs.optional] includeGreetings: bool,
};

[@genType.import ("aws-amplify-react", "withAuthenticator")] [@react.component]
external make:(
    ~props:props,
  ) => React.element = "withAuthenticator";
let default = make;

В Demo.re я использую привязку следующим образом:

let props = {
  WithAuthenticator.props(
    ~comp={
      <App />;
    },
    ~includeGreetings=true,
    (),
  );
};
Js.log(props);
[@react.component]
let app = () => <WithAuthenticator props />;

Затем в App.js я использую Demo.re вот так:

import Amplify from 'aws-amplify';
import {app as App } from './Demo.bs';
import awsconfig from './aws-exports';
import './App.css';
Amplify.configure(awsconfig);

export default App;

Это вызывает следующую ошибку:

 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.
    in withAuthenticator (created by Demo$app)
    in Demo$app (at src/index.js:7)

Я хотел бы понять, что это значит, чтобы разобраться, когда он снова появится.

Это то, что скомпилированный код Bucklescript находится в Demo.bs.js:

// Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
'use strict';

var React = require("react");
var App$ReactHooksTemplate = require("./App.bs.js");
var WithAuthenticator$ReactHooksTemplate = require("../aws/WithAuthenticator.bs.js");

var props = {
  Comp: React.createElement(App$ReactHooksTemplate.make, { }),
  includeGreetings: true
};

console.log(props);

function Demo$app(Props) {
  return React.createElement(WithAuthenticator$ReactHooksTemplate.make, {
              props: props
            });
}

var app = Demo$app;

exports.props = props;
exports.app = app;
/* props Not a pure module */

Воспроизведение этой проблемы можно найти здесь.

Обновлять:

Здесь я пытаюсь следить за комментариями / ответом @ glennsl ниже.

// define a type modeling what `withAuthenticator` is expecting
[@bs.deriving abstract]
type props = {
  [@bs.as "Comp"]
  comp: React.element,
  [@bs.optional]
  includeGreetings: bool,
};
// use bs.module instead of gentype
[@bs.module ("aws-amplify-react", "withAuthenticator")]
external withAuthenticator: props => React.component(props) =
  "withAuthenticator";
module AppWithAuthenticator = {
  [@bs.obj]
  external makeProps:
    (~children: 'children, unit) => {. "children": 'children} =
    "";
  let make = props => withAuthenticator(props);
};

Вот как он может использоваться, но не компилируется.


module AppWithAuth = {
  let props = {
    props(
      ~comp={
        <App />;
      },
      ~includeGreetings=true,
      (),
    );
  };
  [@react.component]
  let make = () => {
    <AppWithAuthenticator props />;
  };
};

ошибка компиляции:

>>>> Start compiling
[1/3] Building src/aws/AuthenticatorBS-ReactHooksTemplate.cmj

  We've found a bug for you!
  /Users/prisc_000/working/DEMOS/my-app/src/aws/AuthenticatorBS.re 34:6-25

  32 │   [@react.component]
  33 │   let make = () => {
  34 │     <AppWithAuthenticator props />;
  35 │   };
  36 │ };

  This call is missing an argument of type props

person armand    schedule 18.08.2019    source источник
comment
withAuthenticator - конструктор компонентов более высокого порядка. Функция, которая при применении к компоненту возвращает другой компонент. Вы не можете использовать его напрямую.   -  person glennsl    schedule 19.08.2019
comment
Спасибо, @glennsl. Можете ли вы указать мне на ресурс / пример привязки к HOC?   -  person armand    schedule 19.08.2019
comment
Я не могу, так как не видел ни одного. Я уверен, что он где-то существует, поскольку не похоже, что это должно быть так сложно с новым API. Возможно, я смогу подробнее изучить это завтра, если вы к тому времени не догадались.   -  person glennsl    schedule 19.08.2019
comment
Спасибо, сэр. Буду обновлять, если что-нибудь придумаю. Сейчас смотрю несколько постов.   -  person armand    schedule 19.08.2019


Ответы (2)


Что-то в этом роде должно работать:

[@genType.import ("aws-amplify-react", "withAuthenticator")]
external withAuthenticator : (React.component('a), bool) => React.component('a) = "withAuthenticator";

module AppWithAuthenticator = {
  [@bs.obj]
  external makeProps: (~children: 'children=?, unit) => {. "children": 'children } = "";
  let make = withAuthenticator(App.make, true);
};

ReactDOMRe.renderToElementWithId(<AppWithAuthenticator />, "root");

external withAuthenticator : ... объявляет внешний конструктор HOC как функцию, которая принимает компонент реакции и bool, и возвращает компонент, который будет принимать одни и те же свойства из-за того, что переменная типа 'a используется в обеих позициях.

module AppWithAuthenticator ... применяет конструктор HOC к App компоненту и настраивает его так, чтобы его можно было использовать с JSX. Это в основном то же самое, что и непосредственный импорт компонента реакции, за исключением того, что мы получаем внешний компонент посредством вызова функции, а не импортируем его напрямую.

Наконец, последняя строка просто демонстрирует, как это можно использовать.

Обратите внимание, что я, очевидно, не тестировал это должным образом, так как у меня нет проекта, настроенного с помощью aws-amplify и тому подобного. Я также никогда не использовал genType, но для этого варианта использования это кажется довольно простым.

person glennsl    schedule 19.08.2019
comment
когда вы говорите, что для этого требуются одни и те же реквизиты, является ли bool опорой в этом примере? Ожидает ли 'a справа то, что когда-либо будет передано как' a или 'a и bool? - person armand; 19.08.2019
comment
Нет, это обычный аргумент функции. Реквизиты представлены 'a, который, как ожидается, будет некоего типа объекта js. К сожалению, в Reason невозможно расширить тип объекта, но при необходимости вы можете жестко закодировать входные и выходные реквизиты. - person glennsl; 19.08.2019
comment
bs.module или genType.import не должны иметь значения, я думаю. Но ваша withAuthenticator привязка очень изношена. Во-первых, он должен принять компонент в качестве первого аргумента, иначе это вообще не HOC. Затем, как я понимаю из документации, вы можете предоставить объект параметров вместо аргумента bool, что, как я думаю, вы пытаетесь сделать. Но в документации я не вижу никаких вариантов Comp. Однако существует вариант authenticatorComponents. - person glennsl; 19.08.2019
comment
И, наконец, объект параметров не совпадает с объектом props. Они должны быть отдельными. - person glennsl; 19.08.2019
comment
Вы также можете просто привязать к Authenticator компонент напрямую, и пропустите все эти HOC. - person glennsl; 19.08.2019
comment
В настоящее время я пытаюсь связать его напрямую, я получил Comp от github.com/aws-amplify/amplify-js/blob/ - person armand; 19.08.2019
comment
Comp - это компонент, который нужно обернуть, переданный в качестве первого аргумента. Это недопустимое свойство объекта параметров. - person glennsl; 19.08.2019
comment
Моя привязка в ответе должна быть эквивалентна тому, что вы пытаетесь сделать с объектом props options, поскольку includeGreetings - второй аргумент. Я просто упростил это для вас. Однако в долгосрочной перспективе это могло быть слишком просто. - person glennsl; 19.08.2019
comment
спасибо, что нашли время научить. Используя ваш пример, я получаю эту ошибку: ( React.component('props), 'props ) => React.element <root>/node_modules/reason-react/src/React.re Error: This expression has type (~children: 'a) => {. "children": 'a} but an expression was expected of type {. } - person armand; 19.08.2019
comment
Да, извините, предполагается, что вы хотите иметь дочерние компоненты. В противном случае вы можете сделать его необязательным или просто удалить из makeProps. - person glennsl; 19.08.2019

Причина разлада канал снова поражает. Это решение работает:

[@bs.module "aws-amplify-react"]
external withAuthenticator:
  // takes a react component and returns a react component with the same signature
  React.component('props) => React.component('props) =
  "withAuthenticator";

module App = {
  [@react.component]
  let make = (~message) => <div> message->React.string </div>;
};

module WrappedApp = {
  include App;
  let make = withAuthenticator(make);
};

И если вы хотите передать вторую опору includeGreeting, как в ответе @glennsl:

[@bs.module "aws-amplify-react"]
external withAuthenticator:
  // takes a react component and returns a react component with the same signature
  (React.component('props), bool) => React.component('props) =
  "withAuthenticator";

module App = {
  [@react.component]
  let make = (~message) => <div> message->React.string </div>;
};

module WrappedApp = {
  include App;
  let make = withAuthenticator(make,true);
};

Вы бы назвали это с помощью:

ReactDOMRe.renderToElementWithId(<WrappedApp message="Thanks" />, "root");

Спасибо @bloodyowl.

И вот как это будет выглядеть, если вы не используете include. См. Комментарий @ glennsl ниже.

module WrappedApp = {
  let makeProps = App.makeProps;
  let make = withAuthenticator(App.make,true);
};
person armand    schedule 19.08.2019
comment
Хм, умное использование include App для наследования makeProps. Но есть ли другие отличия, кроме этого и использования bs.module вместо genType.import? Решения кажутся в основном эквивалентными. - person glennsl; 19.08.2019
comment
Я не могу ответить на этот вопрос, брат. Метод импорта не имеет значения. Единственная разница, возможно, заключалась в том, что одно решение было достаточно простым, чтобы я мог его понять. Я хотел бы знать, как будет работать ваше решение. Как бы выглядел ответ @ bloodyOwl при использовании вашего метода? Я не мог заставить его работать. Я хотел бы добавить сюда ваш ответ: dev.to/idkjs/ - person armand; 19.08.2019
comment
Это в основном то же самое, за исключением того, что вместо include App; вы должны определить let makeProps вручную, как если бы вы импортировали компонент из js. Или, если WrappedApp должен иметь те же реквизиты, что и App, вы также можете просто псевдоним makeProps из App: let makeProps = App.makeProps;. Это именно то, что делает include App;, за исключением того, что также включает все остальное в App. - person glennsl; 19.08.2019
comment
Я мог бы сделать здесь отдельные вопросы и ответы только по HOC, чтобы объяснить это в более общем плане и избежать всего шума с amplify-aws и тому подобным. - person glennsl; 19.08.2019
comment
Я, со своей стороны, был бы очень признателен за это, сэр. Шум aws возникает только потому, что это HOC, который мне нужно привязать, чтобы он был средством обучения. - person armand; 19.08.2019
comment
Вот это. Сообщите мне, если что-то еще неясно. - person glennsl; 24.08.2019