profile
viewpoint
Mateusz Burzyński Andarist @livechat Wrocław, Poland

Andarist/babel-plugin-annotate-pure-calls 60

This plugins helps with annotating top level functions calls with #__PURE__ comment.

aaronshaf/react-callbag-subject 8

Asynchronous pipelines in React using callbags

aaronshaf/callbag-gamepads 7

Callbag source for connected gamepad inputs

aaronshaf/callbag-animation-frames 5

Callbag listenable source sending DOMHighResTimeStamp at display refresh rate

aaronshaf/callbag-keyboard 3

Callbag source for the keyboard

Andarist/are-hook-inputs-equal 3

A comparator function for previous and current React hook inputs.

aaronshaf/callbag-flatten-iter 2

Callbag operator that flattens iterables

Andarist/babel-check-duplicated-nodes 1

🐠 Babel helper module for transforms authors to check the AST against duplicated nodes.

issue commentdavidkpiano/xstate

Parent is not sent on spawned machines

Just to be clear, the symptom here is opposite of #1125 but it appears to be due to the same cause?

Yes - I can confirm that the root cause is the same. The symptoms are actually also the same, but it's slightly obscured by how the "test case" is presented in #1125. The problem is not that you can't send an event to the child, but only that child is not executing its actions. Take a look at this adjusted demo and console output after clicking the button twice: https://codesandbox.io/s/xstate-react-reddit-example-with-actors-l0r4q?file=/src/machine.js

Blacktiger

comment created time in 7 hours

issue commentemotion-js/emotion

[jest-emotion] snapshots reordering css based on test order, causing failures.

Yes - changing this on the next branch would probably be best. Could you prepare a PR with the change? We could discuss this further then.

Jimmydalecleveland

comment created time in 7 hours

issue commentemotion-js/emotion

CSS prop as object in a composition context

You just shouldn't use css there, you can return an array of functions. Take a look at the adjusted example: https://codesandbox.io/s/emotion-typescript-example-bugij?file=/src/Box.tsx

samuelcastro

comment created time in 7 hours

pull request commentrollup/plugins

feature(commonjs): support for destructuring require for named imports

I don't know - this IMHO is a dangerous thing to do. While it can improve some scenarios it can lead to obscure bugs in others - I certainly wouldn't like to bump into an issue caused by this as a developer.

The node ecosystem is pushing towards very strict rules in terms of CJS and ESM interoperability and I feel that introducing yet-another-non-strict semantic to the ecosystem at this point in time shouldn't be discouraged. The issue presented by @LarsDenBakker is IMHO a very good argument against this.

danielgindi

comment created time in 7 hours

issue commentdavidkpiano/xstate

Unable to pass invoked machines (children) down to child components in React

I'm confused, in the linked codesandbox I'm using current.matches('list') && <List service={current.children.listMachine} /> to render the list component only when we should have a child machine. How is that not conditionally rendered?

Sorry, I had to misread the code in a hurry.

When you change it to explicit current.children.listMachine && <List service={current.children.listMachine} /> it sort of works better, but then there is a problem I outlined above.

This doesn't matter - this.children keys are set immediately.


This problem is one of several caused by the latest changes to make all actions (including invoke) scheduled through React's commit phase - we are reverting this and providing means to actually schedule a particular action through React's commit phase in https://github.com/davidkpiano/xstate/pull/1202 . When it lands I will make sure to add a test covering for the scenario described here.

Blacktiger

comment created time in 7 hours

issue commentemotion-js/emotion

CSS prop as object in a composition context

Could you share a repro case? It's hard to tell where the problem is from that snippet of code alone.

samuelcastro

comment created time in 9 hours

issue commentatlassian/changesets

Provide documentation for packages/changelog-github

This is how you can use it: https://github.com/emotion-js/emotion/blob/e09fb216180063f1cad4fafe390e770e7398f811/.changeset/config.json#L3-L6

I agree though that documentation is lacking in this regard - all config options should be documented. They even kinda are in a form of JSON schema that is available here but it's rather hard to reach this when skimming through the documentation (as it's not even part of the actual docs).

brookescarlett

comment created time in 9 hours

Pull request review commentatlassian/changesets

Add support for `ignored` config

 let importantEnd = chalk.red(   "----------------------------------------------------------------------" ); -export default async function version(cwd: string, config: Config) {+export default async function version(+  cwd: string, +  { ignore }: { ignore?: string[] },+  config: Config) {+  // Commandline arguments take precedence over config object,

The CLI flag is supposed to help this - https://github.com/atlassian/changesets/issues/286, for which having to update the config file, doing the publish and reverting changes to the config file seems like a bad DX.

I'm totally fine with disallowing mixing those 2 (config option and CLI flag) ever, such decision shouldn't affect any expectations because we won't provide any default strategy for handling this - instead, we will just fail fast and hard.

Feiyang1

comment created time in 16 hours

issue closeddavidkpiano/xstate

Event action invoked after transient transition guard evaluation

Description I created an already closed issue #1199. I investigated the issue further and identified the root cause, which I believe, is a bug.

Given a simplified example (the use case is described in #1199)

const loginMachine = Machine({
  id: "login",
  initial: "pin",
  states: {
    pin: {
      on: {
        "": [
          { cond: "pinNotSet", target: "password" },
          {
            cond: {
              type: "maxPinFailedAttemptsReached",
              maxFailedAttemptsCount: 5
            },
            actions: [
              "notifyMaxFailedPinAttemptsReached",
              "resetPinFailedAttemptsCount"
            ]
          }
        ],
        SUCCESS: "userAuthenticated",
        FAIL: {
          actions: ["incrementPinFailedAttemptsCount"]
        }
      }
    },
    password: {
      on: {
        SUCCESS: "userAuthenticated"
      }
    },
    userAuthenticated: {
      entry: ["resetPinFailedAttemptsCount"],
      type: "final"
    }
  }
});

We are in pin state. Null transition checks, if user failed to enter PIN more than 5 times. When user enters wrong PIN, the form sends FAIL event, which triggers incrementPinFailedAttemptsCount event action. The context.failedAttemptsCount is incremented via assign action. Because the FAIL transition has no target, it is an internal transition, and the pins state null transition reevaluates maxPinFailedAttemptsReached.

Please, see the example in CodeSandbox, open the console, click on the <kbd>SEND</kbd> button and observe, that everything works as expected, the ACTION incrementPinFailedAttemptsCount is executed before GUARD maxPinFailedAttemptsReached. Nice.

But in my implementation, I persist the failedAttemptsCount in device storage, so user cannot simply reset failedAttemptsCount with app refresh. Because failedAttemptsCount is not hold in machine's context, I don't use assign action, but I use function expressions

{
  actions: {
    incrementPinFailedAttemptsCount: () => {
      const storedValue = storage.get('failedAttemptsCount');
      storage.set('failedAttemptsCount', storedValue + 1);
    }
  }
}

I find out, that using this function expression instead assign action postpones the action, it is executed after GUARD maxPinFailedAttemptsReached. Guard then works with data not updated by transition's action. I think it is a bug, since approach with assign action executes action and guard in correct order.

See CodeSandbox where I emulate storage with useState hook.

Expected Result The (internal) transition's action written as function expression is executed before state's NULL event.

Actual Result The (internal) transition's action written as function expression is executed after state's NULL event and thus transient transition conds are evaluated with wrong data.

Reproduction Storing data in context and using assign action works as expected, ACTION incrementPinFailedAttemptsCount is executed before GUARD maxPinFailedAttemptsReached. See console and click <kbd>SEND</kbd> in https://codesandbox.io/s/stoic-grass-278ke?file=/src/App.js.

Storing data outside context and using ad-hoc function expression as transtition's action does not work. ACTION incrementPinFailedAttemptsCount is executed after GUARD maxPinFailedAttemptsReached. See console and click <kbd>SEND</kbd> in https://codesandbox.io/s/reverent-dream-4y7vl?file=/src/App.js

closed time in 17 hours

czabaj

issue commentdavidkpiano/xstate

Event action invoked after transient transition guard evaluation

I'm afraid that currently, this behavior won't change - the API is not supposed to be used like that and I would recommend only ever using structures provided by a machine (context, event, etc) to implement your guards. It should be fairly easy to just store your counter inside the context by using assign and only syncing the current counter to the localStorage when needed.

Keep also in mind that your repro case which uses React is even worse than the localStorage example because React's state is async and there is literally nothing we could do to read its updated value from within that guard as the change won't update until next render and the guard has to be evaluated immediately.

I'm going to close this issue as it's not actionable from our perspective, but feel free to discuss this further if you still have any doubts - I will make sure to get back to you regardless of the issue being closed.

czabaj

comment created time in 17 hours

Pull request review commentatlassian/changesets

Add support for `ignored` config

 export function incrementVersion(   release: InternalRelease,   preInfo: PreInfo | undefined ) {-  let version =-    release.type === "none"-      ? release.oldVersion-      : semver.inc(release.oldVersion, release.type)!;+  if (release.type === "none") {

this change could use a test for a release.type === 'none' && preInfo !== undefined && preInfo.state.mode !== "exit" case

before the change, this would increment the version, but we don't want to do that

Feiyang1

comment created time in 17 hours

Pull request review commentatlassian/changesets

Add support for `ignored` config

 function assembleReleasePlan(   }; } +// Changesets that contains both ignored and not ignored packages are not allowed+function validateChangesets(+  changesets: NewChangeset[],+  ignored: Readonly<string[]>+): void {+  for (const changeset of changesets) {+    // Using the following 2 arrays to decide whether a changeset+    // contains both ignored and not ignored packages+    const ignoredPackages = [];+    const notIgnoredPackages = [];+    for (const release of changeset.releases) {+      if (+        ignored.find(ignoredPackageName => ignoredPackageName === release.name)+      ) {+        ignoredPackages.push(release.name);+      } else {+        notIgnoredPackages.push(release.name);+      }+    }++    if (ignoredPackages.length > 0 && notIgnoredPackages.length > 0) {+      throw new Error(+        `Found mixed changeset ${changeset.id}\n` ++          `Found ignored packages: ${ignoredPackages.join(" ")}\n` ++          `Found not Ignored packages: ${notIgnoredPackages.join(" ")}\n` +
          `Found not ignored packages: ${notIgnoredPackages.join(" ")}\n` +
Feiyang1

comment created time in 18 hours

Pull request review commentatlassian/changesets

Add support for `ignored` config

 function assembleReleasePlan(   // releases is, at this point a list of all packages we are going to releases,   // flattened down to one release per package, having a reference back to their   // changesets, and with a calculated new versions-  let releases = flattenReleases(changesets, packagesByName);+  let releases = flattenReleases(changesets, packagesByName, config.ignored);    if (updatedPreState !== undefined) {     if (updatedPreState.mode === "exit") {       for (let pkg of packages.packages) {-        if (preVersions.get(pkg.packageJson.name) !== -1) {+        if (preVersions.get(pkg.packageJson.name)) {

❤️

Feiyang1

comment created time in 18 hours

Pull request review commentatlassian/changesets

Add support for `ignored` config

 describe("assemble-release-plan", () => {     expect(releases[1].name).toEqual("pkg-b");     expect(releases[1].newVersion).toEqual("2.0.0");   });+  it("should assemble release plan without ignored packages", () => {+    setup.addChangeset({+      id: "big-cats-delight",+      releases: [{ name: "pkg-a", type: "major" }]+    });+    setup.addChangeset({+      id: "small-dogs-sad",+      releases: [{ name: "pkg-b", type: "minor" }]+    });+    const { releases } = assembleReleasePlan(+      setup.changesets,+      setup.packages,+      {+        ...defaultConfig,+        ignore: ["pkg-b"]+      },+      undefined+    );++    expect(releases.length).toEqual(1);+    expect(releases[0].name).toEqual("pkg-a");+    expect(releases[0].newVersion).toEqual("2.0.0");+  });+  it("should generate releases with 'none' release type for ignored packages through dependencies", () => {+    setup.updateDependency("pkg-b", "pkg-a", "1.0.0");+    setup.addChangeset({+      id: "big-cats-delight",+      releases: [{ name: "pkg-a", type: "major" }]+    });+    setup.addChangeset({+      id: "small-dogs-sad",+      releases: [{ name: "pkg-b", type: "minor" }]+    });+    const { releases } = assembleReleasePlan(+      setup.changesets,+      setup.packages,+      {+        ...defaultConfig,+        ignore: ["pkg-b"]+      },+      undefined+    );++    expect(releases.length).toEqual(2);+    expect(releases[0].name).toEqual("pkg-a");+    expect(releases[0].newVersion).toEqual("2.0.0");+    expect(releases[1].name).toEqual("pkg-b");+    expect(releases[1].type).toEqual("none");+    expect(releases[1].newVersion).toEqual("1.0.0");+  });+  it("should generate releases with 'none' release type for ignored packages through peerDependencies", () => {+    setup.updatePeerDep("pkg-b", "pkg-a", "1.0.0");+    setup.addChangeset({+      id: "big-cats-delight",+      releases: [{ name: "pkg-a", type: "major" }]+    });+    setup.addChangeset({+      id: "small-dogs-sad",+      releases: [{ name: "pkg-b", type: "minor" }]+    });+    const { releases } = assembleReleasePlan(+      setup.changesets,+      setup.packages,+      {+        ...defaultConfig,+        ignore: ["pkg-b"]+      },+      undefined+    );++    expect(releases.length).toEqual(2);+    expect(releases[0].name).toEqual("pkg-a");+    expect(releases[0].newVersion).toEqual("2.0.0");+    expect(releases[1].name).toEqual("pkg-b");+    expect(releases[1].type).toEqual("none");+    expect(releases[1].newVersion).toEqual("1.0.0");+  });+  it("should generate releases with 'none' release type for ignored packages through devDependencies", () => {+    setup.updateDevDependency("pkg-b", "pkg-a", "1.0.0");+    setup.addChangeset({+      id: "big-cats-delight",+      releases: [{ name: "pkg-a", type: "major" }]+    });+    setup.addChangeset({+      id: "small-dogs-sad",+      releases: [{ name: "pkg-b", type: "minor" }]+    });+    const { releases } = assembleReleasePlan(+      setup.changesets,+      setup.packages,+      {+        ...defaultConfig,+        ignore: ["pkg-b"]+      },+      undefined+    );++    expect(releases.length).toEqual(2);+    expect(releases[0].name).toEqual("pkg-a");+    expect(releases[0].newVersion).toEqual("2.0.0");+    expect(releases[1].name).toEqual("pkg-b");+    expect(releases[1].type).toEqual("none");+    expect(releases[1].newVersion).toEqual("1.0.0");+  });+  // Mixed changesets are the ones that contains both ignored packages and not ignored packages+  it("should throw for mixed changesets", () => {+    setup.addChangeset({+      id: "big-cats-delight",+      releases: [+        { name: "pkg-a", type: "major" },+        { name: "pkg-b", type: "minor" }+      ]+    });++    expect(() =>+      assembleReleasePlan(+        setup.changesets,+        setup.packages,+        {+          ...defaultConfig,+          ignore: ["pkg-b"]+        },+        undefined+      )+    ).toThrowErrorMatchingInlineSnapshot(`+"Found mixed changeset big-cats-delight+Found ignored packages: pkg-b+Found not Ignored packages: pkg-a+Mixed changesets that contain both ignored and not ignored packages are not allowed"+`);+  });+  it("should not generate a release for package that doesn't have changeset and is not a dependent in pre exit mode", () => {

could you clarify what "is not a dependent in pre exit mode" means? Im a little bit unsure, does it just mean that it had no changesets related to it generated during pre mode?

Feiyang1

comment created time in 18 hours

Pull request review commentatlassian-labs/compiled-css-in-js

Replace stylis with postcss

+import postcss, { plugin } from 'postcss';+import autoprefixer from 'autoprefixer';+import cssnano from 'cssnano-preset-default';+import nested from 'postcss-nested';+import whitespace from 'postcss-normalize-whitespace';++const minify = () => {+  const preset = cssnano();+  // We exclude async because we need this to run synchronously as ts transformers aren't async!+  const asyncPluginsToExclude = ['postcss-svgo'];++  return preset.plugins+    .map(([creator]: any) => {+      // replicate the `initializePlugin` behavior from https://github.com/cssnano/cssnano/blob/a566cc5/packages/cssnano/src/index.js#L8+      return creator();+    })+    .filter((plugin: any) => {+      return !asyncPluginsToExclude.includes(plugin.postcssPlugin);+    });+};++const parentOrphenedPseudos = plugin('parent-orphened-pseudos', () => {+  return root => {+    root.walkRules(rule => {+      if (rule.selector.includes(':')) {+        const newSelector = rule.selector+          .split(',')+          .map(part => {+            if (part.match(/^. /)) {+              // If the selector has one characters with a space after it, e.g. "> :first-child" then return early.+              return part;+            }++            if (part.includes(':')) {+              // If the selector starts with a colon or has a space and then a colon prepend an "&"!+              return part.replace(/^:| :/g, '&:');

Yes, rule elements have their computed selectors available in .props property. This holds already computed (expanded) selectors, so for this input:

a,b,c{d,e{color:hotpink;}}

it would have such props:

['a d','b d','c d','a e','b e','c e']

if you want to get "selectors" only for the "current level" then you need to reparse .value which for the inner node here would hold 'd,e' string.

madou

comment created time in 18 hours

issue commentdavidkpiano/xstate

NodeJs memory leak with interpret (xstate>=4.7.0-rc6)

I've recognized the problem successfully - but it's not as trivial to fix this as I would have hoped. Gonna think about possible solutions in the following days.

schemelev

comment created time in a day

issue commentemotion-js/emotion

[jest-emotion] snapshots reordering css based on test order, causing failures.

Both tests share the same underlaying cache which is only populated when styles are about to be rendered (so as lazily as we can get). If you have 2 tests - A and B (in that order) and you render both then you end up with A and B styles injected (in that order), if you skip A then you only have B styles injected and this has messed up your snapshots.

Jimmydalecleveland

comment created time in a day

issue closedAndarist/react-textarea-autosize

ref in ts does not support focus()

Version react-textarea-autosize: 8.0.1.

Hi, we are currently using textarea in a typescript react project. Trying to call focus() on the textarea ref component leads to lint returning a type error Property 'focus' does not exist on type 'TextareaAutosize'.

Ref is being used in a functional component pattern:

import Textarea from 'react-textarea-autosize';

function component(){

  const myRef = useRef<Textarea | null>(null);

  return (
    <Textarea
     ref = {myRef}
    />);

Focus is being called on ref with myRef.current?.focus();

EDIT: On further inspection, this seems to be not a problem with this library. We are currently nesting the TextArea component in a styling layer, which also uses forwardRefs, thereby making the outer TextArea component its inner ref.

closed time in a day

Manonisch

issue commentAndarist/react-textarea-autosize

ref in ts does not support focus()

What you have imported as Textarea is not a type - I suspect that you have @types/react-textarea-autosize installed where this is defined as a class which is acceptable for TS in types position.

The correct way of using this in v8 is to uninstall @types/react-textarea-autosize if you have it installed and do this:

import * as React from 'react';
import Textarea from 'react-textarea-autosize';

function MyComponent() {
  const myRef = React.useRef<HTMLTextAreaElement>();
  return <Textarea ref={myRef} />;
}
Manonisch

comment created time in a day

issue commentdavidkpiano/xstate

useService logs warning about uninitialized service when service is passed to child component

@seloner the problem is that useMachine can only ever work with the same instance of a machine. Machines need to have static config - and it's not possible to change this for a particular useMachine call. The warning exists to avoid confusion that you could actually provide a different argument there - and as you are calling withContext each render it creates a new Machine each render and we can't recognize them being the same between renders so it's up to you to memoize it correctly or use @davidkpiano's suggestion

kevinold

comment created time in a day

Pull request review commentatlassian/changesets

Add support for `ignored` config

 let importantEnd = chalk.red(   "----------------------------------------------------------------------" ); -export default async function version(cwd: string, config: Config) {+export default async function version(+  cwd: string, +  { ignore }: { ignore?: string[] },+  config: Config) {+  // Commandline arguments take precedence over config object,

Maybe we should just disallow mixing those 2 right now so we don't have to make this decision right now? @mitchellhamilton @Noviny what do you think - what would be the best behavior for this?

Feiyang1

comment created time in a day

Pull request review commentatlassian/changesets

Add support for `ignored` config

 const cwd = process.cwd();         return;       }       case "version": {-        await version(cwd, config);+        let ignoreArrayFromCmd: undefined | string[] = undefined;+        if (typeof ignore === 'string') {+          ignoreArrayFromCmd = [ignore];+        } else { // undefined or an array+          ignoreArrayFromCmd = ignore;+        }++        // Validate that items in ignoreArrayFromCmd are valid project names+        let pkgNames = new Set(+          packages.packages.map(({ packageJson }) => packageJson.name)+        );+        for (let pkgName of ignoreArrayFromCmd || []) {+          if (!pkgNames.has(pkgName)) {+            error(

Right - doing the whole messages stuff is not needed right now. I have meant that right now this validation only reports a single package name that is not part of the project, but it could actually report all of them.

Feiyang1

comment created time in a day

issue commentemotion-js/emotion

[jest-emotion] snapshots reordering css based on test order, causing failures.

getStylesFromClassNames currently works by iterating through extracted classNames: https://github.com/emotion-js/emotion/blob/fc119766b539f73546dc8e2863cca1439af3cb1c/packages/jest/src/utils.js#L168 and appending matches to the output string, so yes - this currently is prone to style elements being reordered.

A possible solution to this would be to change how this is being iterated in a way that would preserve the order of input classNames. Would you like to take a stab at this?

Jimmydalecleveland

comment created time in a day

Pull request review commentdavidkpiano/xstate

[@xstate/react] Add asEffect and asLayoutEffect

 A [React hook](https://reactjs.org/hooks) that subscribes to state changes from - `state` - Represents the current state of the service as an XState `State` object. - `send` - A function that sends events to the running service. -### `useMachine(machine)` with `@xstate/fsm` <Badge text="1.1+"/>+### `asEffect(action)`

If this is not intended to work then we should try to implement a warning if somebody tries to do this.

However, I would be quite surprised that I wouldn't be able to use this for a nested machine in an example like this: https://codesandbox.io/embed/33wr94qv1 (the official TODO example). This limitation is not apparent at all to a user, so we should at least try to surface it is a problem.

davidkpiano

comment created time in 2 days

Pull request review commentdavidkpiano/xstate

[@xstate/react] Add asEffect and asLayoutEffect

+import { useState, useEffect, useRef, useLayoutEffect } from 'react';+import {+  interpret,+  EventObject,+  StateMachine,+  State,+  Interpreter,+  InterpreterOptions,+  MachineOptions,+  StateConfig,+  Typestate,+  ActionObject,+  ActionFunction,+  ActionMeta+} from 'xstate';+import useConstant from './useConstant';+import { partition } from 'xstate/lib/utils';++enum ReactEffectType {+  Effect = 1,+  LayoutEffect = 2+}++export interface ReactActionFunction<TContext, TEvent extends EventObject> {+  (+    context: TContext,+    event: TEvent,+    meta: ActionMeta<TContext, TEvent>+  ): () => void;+  __effect: ReactEffectType;+}++export interface ReactActionObject<TContext, TEvent extends EventObject>+  extends ActionObject<TContext, TEvent> {+  exec: ReactActionFunction<TContext, TEvent>;+}++export function asEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `effect:${exec.name}` },+    __effect: { value: ReactEffectType.Effect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export function asLayoutEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `layoutEffect:${exec.name}` },+    __effect: { value: ReactEffectType.LayoutEffect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export type ActionStateTuple<TContext, TEvent extends EventObject> = [+  ReactActionObject<TContext, TEvent>,+  State<TContext, TEvent>+];++function executeEffect<TContext, TEvent extends EventObject>(+  action: ReactActionObject<TContext, TEvent>,+  state: State<TContext, TEvent>+): void {+  const { exec } = action;+  const originalExec = exec!(state.context, state._event.data, {+    action,+    state: state,+    _event: state._event+  });++  originalExec();+}++interface UseMachineOptions<TContext, TEvent extends EventObject> {+  /**+   * If provided, will be merged with machine's `context`.+   */+  context?: Partial<TContext>;+  /**+   * The state to rehydrate the machine to. The machine will+   * start at this state instead of its `initialState`.+   */+  state?: StateConfig<TContext, TEvent>;+}++export function useMachine<+  TContext,+  TEvent extends EventObject,+  TTypestate extends Typestate<TContext> = any+>(+  machine: StateMachine<TContext, any, TEvent, TTypestate>,+  options: Partial<InterpreterOptions> &+    Partial<UseMachineOptions<TContext, TEvent>> &+    Partial<MachineOptions<TContext, TEvent>> = {}+): [+  State<TContext, TEvent, any, TTypestate>,+  Interpreter<TContext, any, TEvent, TTypestate>['send'],+  Interpreter<TContext, any, TEvent, TTypestate>+] {+  if (process.env.NODE_ENV !== 'production') {+    const [initialMachine] = useState(machine);++    if (machine !== initialMachine) {+      console.warn(+        'Machine given to `useMachine` has changed between renders. This is not supported and might lead to unexpected results.\n' ++          'Please make sure that you pass the same Machine as argument each time.'+      );+    }+  }++  const {+    context,+    guards,+    actions,+    activities,+    services,+    delays,+    state: rehydratedState,+    ...interpreterOptions+  } = options;++  const service = useConstant(() => {+    const machineConfig = {+      context,+      guards,+      actions,+      activities,+      services,+      delays+    };++    const createdMachine = machine.withConfig(machineConfig, {+      ...machine.context,+      ...context+    } as TContext);++    return interpret(createdMachine, interpreterOptions).start(+      rehydratedState ? State.create(rehydratedState) : undefined+    );+  });++  const [state, setState] = useState(service.state);++  const effectActionsRef = useRef<+    Array<[ReactActionObject<TContext, TEvent>, State<TContext, TEvent>]>+  >([]);+  const layoutEffectActionsRef = useRef<+    Array<[ReactActionObject<TContext, TEvent>, State<TContext, TEvent>]>+  >([]);++  useLayoutEffect(() => {+    service.onTransition((currentState) => {+      if (currentState.changed) {+        setState(currentState);+      }++      if (currentState.actions.length) {+        const reactEffectActions = currentState.actions.filter(+          (action): action is ReactActionObject<TContext, TEvent> => {+            return (+              typeof action.exec === 'function' &&+              '__effect' in (action as ReactActionObject<TContext, TEvent>).exec+            );+          }+        );++        const [effectActions, layoutEffectActions] = partition(+          reactEffectActions,+          (action): action is ReactActionObject<TContext, TEvent> => {+            return action.exec.__effect === ReactEffectType.Effect;+          }+        );++        effectActionsRef.current.push(+          ...effectActions.map<ActionStateTuple<TContext, TEvent>>(+            (effectAction) => [effectAction, currentState]+          )+        );++        layoutEffectActionsRef.current.push(+          ...layoutEffectActions.map<ActionStateTuple<TContext, TEvent>>(+            (layoutEffectAction) => [layoutEffectAction, currentState]+          )+        );+      }+    });++    // if service.state has not changed React should just bail out from this update+    setState(service.state);++    return () => {+      service.stop();+    };+  }, []);++  useLayoutEffect(() => {+    while (layoutEffectActionsRef.current.length) {

While doing asEffect(() => send(FOO)) is definitely a weird thing to do, so it's kinda unrealistic, it can be somewhere deep down in the stack. But flattening more complex case which could happen in a real app to this simplistic asEffect(() => send(FOO)) leads me to believe that an effect action could get in fact be executed in the middle of this flushing.

After all, this is being flushed whenever React decides to do so, so XState is not aware of this, it is happening outside of its internal scheduler which means that first send will execute immediately which in turn will cause onTransition to be called synchronously (still while flushing) and this might lead to a new effect action to be queued at the end of the currently flushed items list.

davidkpiano

comment created time in 2 days

issue commentdavidkpiano/xstate

Parent is not sent on spawned machines

Well - I assume that when one develops an application in React they need to learn how it works. I agree though that even knowing React quite well this might not be that obvious when to wrap things with asEffect because when abstracting the behavior with machines you kinda disconnect yourself from React (mentally). When developing regular React components it's much easier to feel this stuff intuitively because you are closer to the bare bones and you directly see if you are executing something in event handlers or somewhere else whereas with XState you always just send events and things are handled in a separate place (the machine). I don't particularly feel that's a big issue though as machines used by useMachine are usually tightly coupled to the UI and they mostly receive and respond to events generated within DOM event handlers. Introducing asEffect is only helpful for a couple of cases - mainly when needing to work with host elements comparatively, like for example when you need to focus input and only when it doesn't exist yet because if it exists you can just call .focus() on it immediately. So to sum it up - I don't this shouldn't be needed that often and once you need it it should be quite obvious what's the problem at hand (that you are in a moment when things are not ready to be used yet) and thus the solution (using asEffect) should come naturally quite quickly. To some extent this might feel like "trial & fail" when you fail to recognize the need for this when modeling but I think those situations should be quite spare. It's, of course, hard to quantify this though right now - the future will tell us if this gets people confusing for confused or not.

Blacktiger

comment created time in 2 days

issue commentdavidkpiano/xstate

Parent is not sent on spawned machines

The current RC version is scheduling all actions through React's effect callbacks. This is not necessarily good. If you are interested in my thoughts about this you can dive into this: <details> <summary>lengthy explanation 😅</summary>

I'd like to discuss how executing actions should be done when using @xstate/react. This seems like not an obvious decision, concerning how little actual experience we have with Concurrent Mode and any discussion about this so far has been based more on our thoughts, hypothesis, materials shared by React team and some discussions with them as well. ​ Problem is that code written in a more traditional way, with more traditional assumptions, often doesn't blend OK with what React is trying to enforce. The main difference is that sometimes executing side-effects is being considered unsafe because it might lead to them being executed more than once (and this is not something that we want). It's worth mentioning why this can happen - everything boils down to a fact that React can call render multiple times and thus any side-effects executed during render phase is being considered unsafe. ​ How one can deal with this problem? The answer is "simple" - schedule those side-effects to be executed during commit phase (inside React's effect callbacks). Does it mean that all side-effects should be scheduled through effects? Not really and React team member has confirmed that. ​ Let's recap the information gathered so far:

  1. Do we have to schedule all actions (our side effects) through effect callbacks? No
  2. Can we do it? Yes, it's our design decision that we can make
  3. Should we do it? It's uncertain, but I'm leaning towards - no.Why I don't believe this is a good thing for us to schedule all side-effects for commit phase
  4. There are valid cases when one would like to execute asap. I don't feel like the decision about this should be ours - it should lie in developers' hands.
  5. It's not explicit and it's a magical - so it stands against what we are trying to do: promote explicitness.
  6. As it is not explicit the behavior of useMachine differs between it and regular machines, forces people to think about 2 different scheduling mechanism. It's like learning 2 separate APIs (to some extent) and moving machines between React and non-React stops to be seamless. Yes, it might not require changes most of the time, but there might be cases when it might matter and by making React behavior different we lose the ease of spotting places where it does and which places should be reconsidered.
  7. It might cause some actions to get lost. Think about sending telemetry events just before machine unmounts - if we dont get to next render and thus next commit phase the action in question gets lost inadvertently, which might be easily missed and might not cover behavior requirements ideally.
  8. It leads to poorer performance for some cases as all actions needs to trigger rerender (and thus React need to make some rerendering work which could easily be avoided). A simple example of this would be focusing an existing input after click on a button - in this case there is no reason to postpone the execution of this focus action, it might even lead to deteriorated user experience. The expectancy for this scenario is for the input being focused immediately whereas if this gets scheduled for the next commit phase we don't know when it will execute. It probably should get fired "soon enough" but there is no guarantee of that
  9. It might even lead to runtime errors and crashing the component. Consider the same example from the previous point - if there is arbitrary work happening between click on a button and actual focus action being executed then there is no guarantee that the input in question still exists, it could have been removed in between (state could have changed for some reason leading to different render output). ​ Is postponing side-effects till the commit phase a bad idea? ​ Not entirely. There are valid cases when it is preferred to schedule an action for the commit phase, but I believe that this should be done through a dedicated API - which would be explicit (for example some extra scheduleForCommit action creator) and wouldn't as easily lead to the outlined pitfalls. More than that - we could try to introduce dev-only warnings when we detect misusage and actions being executed at bad times (which is only during rendering). This might not be possible for all cases, but should at least be doable for initial actions (which currently always happen during rendering). </details>

What is actually happening in that linked PR is that we are reverting this behavior and we get back to firing actions as we go but we provide a specialized action creator which allows you to schedule a particular action to be executed during React's commit phase.

Blacktiger

comment created time in 2 days

Pull request review commentatlassian/changesets

Add support for `ignored` config

 function assembleReleasePlan(   return {     changesets,     releases: [...releases.values()].map(incompleteRelease => {-      return {-        ...incompleteRelease,-        newVersion: incrementVersion(incompleteRelease, preInfo)!-      };+      // For ignored packages, the newVersion is the same as the old version+      // in both prereleased and regular releases+      if (isIgnoredPackage(incompleteRelease.name, config.ignored)) {

incrementVersion() currently keeps executing and will give release.type === 'none' packages a new version in prereleases. Is it a valid use case?

I would say that no, it's not a valid use case - at least I don't see why one would want that. Good catch! So yes - I would make this an early return in incrementVersion

Feiyang1

comment created time in 2 days

Pull request review commentatlassian/changesets

Add support for `ignored` config

 const cwd = process.cwd();         return;       }       case "version": {-        await version(cwd, config);+        let ignoreArrayFromCmd: undefined | string[] = undefined;+        if (typeof ignore === 'string') {+          ignoreArrayFromCmd = [ignore];+        } else { // undefined or an array+          ignoreArrayFromCmd = ignore;+        }++        // Validate that items in ignoreArrayFromCmd are valid project names+        let pkgNames = new Set(+          packages.packages.map(({ packageJson }) => packageJson.name)+        );+        for (let pkgName of ignoreArrayFromCmd || []) {+          if (!pkgNames.has(pkgName)) {+            error(

Take a look how config validation gathers all errors: https://github.com/atlassian/changesets/blob/b876789f0a3a9913db5ee86e5fc76c5560529c82/packages/config/src/index.ts#L162

This strategy is quite cool as it allows surfacing all existing problems at once rather than surfacing one, user retrying the command after fixing their problem and bumping into another one immediately.

Feiyang1

comment created time in 2 days

Pull request review commentatlassian/changesets

Add support for `ignored` config

 const { input, flags } = meow(   Commands     init     add [--empty]-    version+    version [--ignore]

Interesting - I've assumed previously that it would be a publish flag, but I think this makes more sense and makes the implementation more unified 👌

Feiyang1

comment created time in 2 days

Pull request review commentatlassian/changesets

Add support for `ignored` config

 let importantEnd = chalk.red(   "----------------------------------------------------------------------" ); -export default async function version(cwd: string, config: Config) {+export default async function version(+  cwd: string, +  { ignore }: { ignore?: string[] },+  config: Config) {+  // Commandline arguments take precedence over config object,

I would prefer merging both - it would be a surprise to me that one-off usage (through CLI) would override the one that is committed to the config file.

Feiyang1

comment created time in 2 days

Pull request review commentatlassian/changesets

Add support for `ignored` config

 function assembleReleasePlan(   // releases is, at this point a list of all packages we are going to releases,   // flattened down to one release per package, having a reference back to their   // changesets, and with a calculated new versions-  let releases = flattenReleases(changesets, packagesByName);+  let releases = flattenReleases(changesets, packagesByName, config.ignored);    if (updatedPreState !== undefined) {     if (updatedPreState.mode === "exit") {       for (let pkg of packages.packages) {-        if (preVersions.get(pkg.packageJson.name) !== -1) {+        if (preVersions.get(pkg.packageJson.name)) {

I suspect this was a mistake as well - would you be able to share the repro case for the situation you have encountered? It would be great to add a test covering your fix.

Feiyang1

comment created time in 2 days

Pull request review commentatlassian/changesets

Add support for `ignored` config

 export type Config = {   baseBranch: string;   /** The minimum bump type to trigger automatic update of internal dependencies that are part of the same release */   updateInternalDependencies: "patch" | "minor";+  ignored: ReadonlyArray<string>;

yeah, that's probably best - and ignore works better for both contexts

Feiyang1

comment created time in 2 days

Pull request review commentdavidkpiano/xstate

[@xstate/react] Add asEffect and asLayoutEffect

 A [React hook](https://reactjs.org/hooks) that subscribes to state changes from - `state` - Represents the current state of the service as an XState `State` object. - `send` - A function that sends events to the running service. -### `useMachine(machine)` with `@xstate/fsm` <Badge text="1.1+"/>+### `asEffect(action)`

Right now this won't work at all for invoked/spawned machines. I have no good idea on how to solve this just yet in a way that would not be involving submachines somehow reaching to the "root" of the created machine-tree. And even with something like this I have a problem of figuring out how to perform this action and keeping XState's core not aware of @xstate/react's special needs.

davidkpiano

comment created time in 2 days

Pull request review commentdavidkpiano/xstate

[@xstate/react] Add asEffect and asLayoutEffect

+import { useState, useEffect, useRef, useLayoutEffect } from 'react';+import {+  interpret,+  EventObject,+  StateMachine,+  State,+  Interpreter,+  InterpreterOptions,+  MachineOptions,+  StateConfig,+  Typestate,+  ActionObject,+  ActionFunction,+  ActionMeta+} from 'xstate';+import useConstant from './useConstant';+import { partition } from 'xstate/lib/utils';++enum ReactEffectType {+  Effect = 1,+  LayoutEffect = 2+}++export interface ReactActionFunction<TContext, TEvent extends EventObject> {+  (+    context: TContext,+    event: TEvent,+    meta: ActionMeta<TContext, TEvent>+  ): () => void;+  __effect: ReactEffectType;+}++export interface ReactActionObject<TContext, TEvent extends EventObject>+  extends ActionObject<TContext, TEvent> {+  exec: ReactActionFunction<TContext, TEvent>;+}++export function asEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `effect:${exec.name}` },+    __effect: { value: ReactEffectType.Effect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export function asLayoutEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `layoutEffect:${exec.name}` },+    __effect: { value: ReactEffectType.LayoutEffect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export type ActionStateTuple<TContext, TEvent extends EventObject> = [+  ReactActionObject<TContext, TEvent>,+  State<TContext, TEvent>+];++function executeEffect<TContext, TEvent extends EventObject>(+  action: ReactActionObject<TContext, TEvent>,+  state: State<TContext, TEvent>+): void {+  const { exec } = action;+  const originalExec = exec!(state.context, state._event.data, {

I'm worried that those currently are called with "settled" arguments whereas regular actions are called with the current arguments from the time when a transition was selected. This makes those different from other builtin actions.

It would be IMHO preferable to have those behaving more like the rest of builtin actions. A really quick idea of how we could solve this:

Introduce a new capture action (tentative name). It could then be "resolved" by resolveActions and actually executed by exec in the Interpreter class. In the future, we could consider "capturing" all custom actions to make them behave closer to the builtin ones in this regard. I think this is quite important because it seems quite expected to have FOO event's type here at all times:

on: { FOO: { actions: (ctx, event) => {} } }
davidkpiano

comment created time in 2 days

Pull request review commentdavidkpiano/xstate

[@xstate/react] Add asEffect and asLayoutEffect

+import { useState, useEffect, useRef, useLayoutEffect } from 'react';+import {+  interpret,+  EventObject,+  StateMachine,+  State,+  Interpreter,+  InterpreterOptions,+  MachineOptions,+  StateConfig,+  Typestate,+  ActionObject,+  ActionFunction,+  ActionMeta+} from 'xstate';+import useConstant from './useConstant';+import { partition } from 'xstate/lib/utils';++enum ReactEffectType {+  Effect = 1,+  LayoutEffect = 2+}++export interface ReactActionFunction<TContext, TEvent extends EventObject> {+  (+    context: TContext,+    event: TEvent,+    meta: ActionMeta<TContext, TEvent>+  ): () => void;+  __effect: ReactEffectType;+}++export interface ReactActionObject<TContext, TEvent extends EventObject>+  extends ActionObject<TContext, TEvent> {+  exec: ReactActionFunction<TContext, TEvent>;+}++export function asEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `effect:${exec.name}` },+    __effect: { value: ReactEffectType.Effect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export function asLayoutEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `layoutEffect:${exec.name}` },+    __effect: { value: ReactEffectType.LayoutEffect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export type ActionStateTuple<TContext, TEvent extends EventObject> = [+  ReactActionObject<TContext, TEvent>,+  State<TContext, TEvent>+];++function executeEffect<TContext, TEvent extends EventObject>(+  action: ReactActionObject<TContext, TEvent>,+  state: State<TContext, TEvent>+): void {+  const { exec } = action;+  const originalExec = exec!(state.context, state._event.data, {+    action,+    state: state,+    _event: state._event+  });++  originalExec();+}++interface UseMachineOptions<TContext, TEvent extends EventObject> {+  /**+   * If provided, will be merged with machine's `context`.+   */+  context?: Partial<TContext>;+  /**+   * The state to rehydrate the machine to. The machine will+   * start at this state instead of its `initialState`.+   */+  state?: StateConfig<TContext, TEvent>;+}++export function useMachine<+  TContext,+  TEvent extends EventObject,+  TTypestate extends Typestate<TContext> = any+>(+  machine: StateMachine<TContext, any, TEvent, TTypestate>,+  options: Partial<InterpreterOptions> &+    Partial<UseMachineOptions<TContext, TEvent>> &+    Partial<MachineOptions<TContext, TEvent>> = {}+): [+  State<TContext, TEvent, any, TTypestate>,+  Interpreter<TContext, any, TEvent, TTypestate>['send'],+  Interpreter<TContext, any, TEvent, TTypestate>+] {+  if (process.env.NODE_ENV !== 'production') {+    const [initialMachine] = useState(machine);++    if (machine !== initialMachine) {+      console.warn(+        'Machine given to `useMachine` has changed between renders. This is not supported and might lead to unexpected results.\n' ++          'Please make sure that you pass the same Machine as argument each time.'+      );+    }+  }++  const {+    context,+    guards,+    actions,+    activities,+    services,+    delays,+    state: rehydratedState,+    ...interpreterOptions+  } = options;++  const service = useConstant(() => {+    const machineConfig = {+      context,+      guards,+      actions,+      activities,+      services,+      delays+    };++    const createdMachine = machine.withConfig(machineConfig, {+      ...machine.context,+      ...context+    } as TContext);++    return interpret(createdMachine, interpreterOptions).start(+      rehydratedState ? State.create(rehydratedState) : undefined+    );+  });++  const [state, setState] = useState(service.state);++  const effectActionsRef = useRef<+    Array<[ReactActionObject<TContext, TEvent>, State<TContext, TEvent>]>+  >([]);+  const layoutEffectActionsRef = useRef<+    Array<[ReactActionObject<TContext, TEvent>, State<TContext, TEvent>]>+  >([]);++  useLayoutEffect(() => {+    service.onTransition((currentState) => {+      if (currentState.changed) {+        setState(currentState);+      }++      if (currentState.actions.length) {+        const reactEffectActions = currentState.actions.filter(+          (action): action is ReactActionObject<TContext, TEvent> => {+            return (+              typeof action.exec === 'function' &&+              '__effect' in (action as ReactActionObject<TContext, TEvent>).exec+            );+          }+        );++        const [effectActions, layoutEffectActions] = partition(+          reactEffectActions,+          (action): action is ReactActionObject<TContext, TEvent> => {+            return action.exec.__effect === ReactEffectType.Effect;+          }+        );++        effectActionsRef.current.push(+          ...effectActions.map<ActionStateTuple<TContext, TEvent>>(+            (effectAction) => [effectAction, currentState]+          )+        );++        layoutEffectActionsRef.current.push(+          ...layoutEffectActions.map<ActionStateTuple<TContext, TEvent>>(+            (layoutEffectAction) => [layoutEffectAction, currentState]+          )+        );+      }+    });++    // if service.state has not changed React should just bail out from this update+    setState(service.state);++    return () => {+      service.stop();+    };+  }, []);++  useLayoutEffect(() => {+    while (layoutEffectActionsRef.current.length) {+      const [+        layoutEffectAction,+        effectState+      ] = layoutEffectActionsRef.current.shift()!;++      executeEffect(layoutEffectAction, effectState);+    }+  }, [state]);

nit: Hard to tell without actually measuring this, but I suppose this might even decrease perf a little bit, you need to allocate this temp array on each render, and do the comparison of old and new inputs. I suspect that always calling an effect and doing a simple .length check in it might be better overall.

davidkpiano

comment created time in 2 days

Pull request review commentdavidkpiano/xstate

[@xstate/react] Add asEffect and asLayoutEffect

+import { useState, useEffect, useRef, useLayoutEffect } from 'react';+import {+  interpret,+  EventObject,+  StateMachine,+  State,+  Interpreter,+  InterpreterOptions,+  MachineOptions,+  StateConfig,+  Typestate,+  ActionObject,+  ActionFunction,+  ActionMeta+} from 'xstate';+import useConstant from './useConstant';+import { partition } from 'xstate/lib/utils';

For ESM consumers this might pull in an additional (duplicated) file into their bundles as they are indirectly using xstate/es/utils.js. This is small util and we don't really need it that much here as we could just go with a single iteration over actions

davidkpiano

comment created time in 2 days

Pull request review commentdavidkpiano/xstate

[@xstate/react] Add asEffect and asLayoutEffect

+import { useState, useEffect, useRef, useLayoutEffect } from 'react';+import {+  interpret,+  EventObject,+  StateMachine,+  State,+  Interpreter,+  InterpreterOptions,+  MachineOptions,+  StateConfig,+  Typestate,+  ActionObject,+  ActionFunction,+  ActionMeta+} from 'xstate';+import useConstant from './useConstant';+import { partition } from 'xstate/lib/utils';++enum ReactEffectType {+  Effect = 1,+  LayoutEffect = 2+}++export interface ReactActionFunction<TContext, TEvent extends EventObject> {+  (+    context: TContext,+    event: TEvent,+    meta: ActionMeta<TContext, TEvent>+  ): () => void;+  __effect: ReactEffectType;+}++export interface ReactActionObject<TContext, TEvent extends EventObject>+  extends ActionObject<TContext, TEvent> {+  exec: ReactActionFunction<TContext, TEvent>;+}++export function asEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `effect:${exec.name}` },+    __effect: { value: ReactEffectType.Effect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export function asLayoutEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `layoutEffect:${exec.name}` },+    __effect: { value: ReactEffectType.LayoutEffect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export type ActionStateTuple<TContext, TEvent extends EventObject> = [+  ReactActionObject<TContext, TEvent>,+  State<TContext, TEvent>+];++function executeEffect<TContext, TEvent extends EventObject>(+  action: ReactActionObject<TContext, TEvent>,+  state: State<TContext, TEvent>+): void {+  const { exec } = action;+  const originalExec = exec!(state.context, state._event.data, {+    action,+    state: state,+    _event: state._event+  });++  originalExec();+}++interface UseMachineOptions<TContext, TEvent extends EventObject> {+  /**+   * If provided, will be merged with machine's `context`.+   */+  context?: Partial<TContext>;+  /**+   * The state to rehydrate the machine to. The machine will+   * start at this state instead of its `initialState`.+   */+  state?: StateConfig<TContext, TEvent>;+}++export function useMachine<+  TContext,+  TEvent extends EventObject,+  TTypestate extends Typestate<TContext> = any+>(+  machine: StateMachine<TContext, any, TEvent, TTypestate>,+  options: Partial<InterpreterOptions> &+    Partial<UseMachineOptions<TContext, TEvent>> &+    Partial<MachineOptions<TContext, TEvent>> = {}+): [+  State<TContext, TEvent, any, TTypestate>,+  Interpreter<TContext, any, TEvent, TTypestate>['send'],+  Interpreter<TContext, any, TEvent, TTypestate>+] {+  if (process.env.NODE_ENV !== 'production') {+    const [initialMachine] = useState(machine);++    if (machine !== initialMachine) {+      console.warn(+        'Machine given to `useMachine` has changed between renders. This is not supported and might lead to unexpected results.\n' ++          'Please make sure that you pass the same Machine as argument each time.'+      );+    }+  }++  const {+    context,+    guards,+    actions,+    activities,+    services,+    delays,+    state: rehydratedState,+    ...interpreterOptions+  } = options;++  const service = useConstant(() => {+    const machineConfig = {+      context,+      guards,+      actions,+      activities,+      services,+      delays+    };++    const createdMachine = machine.withConfig(machineConfig, {+      ...machine.context,+      ...context+    } as TContext);++    return interpret(createdMachine, interpreterOptions).start(+      rehydratedState ? State.create(rehydratedState) : undefined+    );+  });++  const [state, setState] = useState(service.state);++  const effectActionsRef = useRef<+    Array<[ReactActionObject<TContext, TEvent>, State<TContext, TEvent>]>+  >([]);+  const layoutEffectActionsRef = useRef<+    Array<[ReactActionObject<TContext, TEvent>, State<TContext, TEvent>]>+  >([]);++  useLayoutEffect(() => {+    service.onTransition((currentState) => {+      if (currentState.changed) {+        setState(currentState);+      }++      if (currentState.actions.length) {+        const reactEffectActions = currentState.actions.filter(+          (action): action is ReactActionObject<TContext, TEvent> => {+            return (+              typeof action.exec === 'function' &&+              '__effect' in (action as ReactActionObject<TContext, TEvent>).exec+            );+          }+        );++        const [effectActions, layoutEffectActions] = partition(+          reactEffectActions,+          (action): action is ReactActionObject<TContext, TEvent> => {+            return action.exec.__effect === ReactEffectType.Effect;+          }+        );++        effectActionsRef.current.push(+          ...effectActions.map<ActionStateTuple<TContext, TEvent>>(+            (effectAction) => [effectAction, currentState]+          )+        );++        layoutEffectActionsRef.current.push(+          ...layoutEffectActions.map<ActionStateTuple<TContext, TEvent>>(+            (layoutEffectAction) => [layoutEffectAction, currentState]+          )+        );+      }+    });++    // if service.state has not changed React should just bail out from this update+    setState(service.state);++    return () => {+      service.stop();+    };+  }, []);++  useLayoutEffect(() => {+    while (layoutEffectActionsRef.current.length) {

I believe that we should move existing queued actions to a local variable and assign a fresh empty array to this ref. If the execution of a queued effect leads to queuing an effect then when should it happen? Within the same flushing operation? Or should it schedule it for another flush within the upcoming render that is going to happen anyway because the state had to be set? I would be in favor of scheduling it for another flush because one might assume that a scheduled effect happens when things (most notably DOM elements) are ready to be used but a particular element might not exist if we just append to the existing flushing operation (imagine asEffect leading [assign, asEffect] where this assign is causing a new element to be rendered)

davidkpiano

comment created time in 2 days

Pull request review commentdavidkpiano/xstate

[@xstate/react] Add asEffect and asLayoutEffect

+import { useState, useEffect, useRef, useLayoutEffect } from 'react';+import {+  interpret,+  EventObject,+  StateMachine,+  State,+  Interpreter,+  InterpreterOptions,+  MachineOptions,+  StateConfig,+  Typestate,+  ActionObject,+  ActionFunction,+  ActionMeta+} from 'xstate';+import useConstant from './useConstant';+import { partition } from 'xstate/lib/utils';++enum ReactEffectType {+  Effect = 1,+  LayoutEffect = 2+}++export interface ReactActionFunction<TContext, TEvent extends EventObject> {+  (+    context: TContext,+    event: TEvent,+    meta: ActionMeta<TContext, TEvent>+  ): () => void;+  __effect: ReactEffectType;+}++export interface ReactActionObject<TContext, TEvent extends EventObject>+  extends ActionObject<TContext, TEvent> {+  exec: ReactActionFunction<TContext, TEvent>;+}++export function asEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `effect:${exec.name}` },+    __effect: { value: ReactEffectType.Effect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export function asLayoutEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `layoutEffect:${exec.name}` },+    __effect: { value: ReactEffectType.LayoutEffect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export type ActionStateTuple<TContext, TEvent extends EventObject> = [+  ReactActionObject<TContext, TEvent>,+  State<TContext, TEvent>+];++function executeEffect<TContext, TEvent extends EventObject>(+  action: ReactActionObject<TContext, TEvent>,+  state: State<TContext, TEvent>+): void {+  const { exec } = action;+  const originalExec = exec!(state.context, state._event.data, {+    action,+    state: state,+    _event: state._event+  });++  originalExec();+}++interface UseMachineOptions<TContext, TEvent extends EventObject> {+  /**+   * If provided, will be merged with machine's `context`.+   */+  context?: Partial<TContext>;+  /**+   * The state to rehydrate the machine to. The machine will+   * start at this state instead of its `initialState`.+   */+  state?: StateConfig<TContext, TEvent>;+}++export function useMachine<+  TContext,+  TEvent extends EventObject,+  TTypestate extends Typestate<TContext> = any+>(+  machine: StateMachine<TContext, any, TEvent, TTypestate>,+  options: Partial<InterpreterOptions> &+    Partial<UseMachineOptions<TContext, TEvent>> &+    Partial<MachineOptions<TContext, TEvent>> = {}+): [+  State<TContext, TEvent, any, TTypestate>,+  Interpreter<TContext, any, TEvent, TTypestate>['send'],+  Interpreter<TContext, any, TEvent, TTypestate>+] {+  if (process.env.NODE_ENV !== 'production') {+    const [initialMachine] = useState(machine);++    if (machine !== initialMachine) {+      console.warn(+        'Machine given to `useMachine` has changed between renders. This is not supported and might lead to unexpected results.\n' ++          'Please make sure that you pass the same Machine as argument each time.'+      );+    }+  }++  const {+    context,+    guards,+    actions,+    activities,+    services,+    delays,+    state: rehydratedState,+    ...interpreterOptions+  } = options;++  const service = useConstant(() => {+    const machineConfig = {+      context,+      guards,+      actions,+      activities,+      services,+      delays+    };++    const createdMachine = machine.withConfig(machineConfig, {+      ...machine.context,+      ...context+    } as TContext);++    return interpret(createdMachine, interpreterOptions).start(+      rehydratedState ? State.create(rehydratedState) : undefined+    );+  });++  const [state, setState] = useState(service.state);++  const effectActionsRef = useRef<+    Array<[ReactActionObject<TContext, TEvent>, State<TContext, TEvent>]>+  >([]);+  const layoutEffectActionsRef = useRef<+    Array<[ReactActionObject<TContext, TEvent>, State<TContext, TEvent>]>+  >([]);++  useLayoutEffect(() => {

This wont quite work OK with SSR - people will get warnings about useLayoutEffect being used on server. We should use „isomorphic layout effect” - basically conditionally use layout or regular effect based on the environment. Its what other libraries do - for example react-redux. I was just preparing a package for this earlier this week (which is just reexporting correct thing, but handles browser, ssr, react-native correctly). We could reuse it or inline its content here - i could handle this once this PR lands as i already know how to do this „best”

davidkpiano

comment created time in 3 days

Pull request review commentdavidkpiano/xstate

[@xstate/react] Add asEffect and asLayoutEffect

  All notable changes to this project will be documented in this file. +## [1.0.0-rc]

This should include the next rc number so people can track changes with ease in the future.

davidkpiano

comment created time in 3 days

Pull request review commentdavidkpiano/xstate

[@xstate/react] Add asEffect and asLayoutEffect

+import { useState, useEffect, useRef, useLayoutEffect } from 'react';+import {+  interpret,+  EventObject,+  StateMachine,+  State,+  Interpreter,+  InterpreterOptions,+  MachineOptions,+  StateConfig,+  Typestate,+  ActionObject,+  ActionFunction,+  ActionMeta+} from 'xstate';+import useConstant from './useConstant';+import { partition } from 'xstate/lib/utils';++enum ReactEffectType {+  Effect = 1,+  LayoutEffect = 2+}++export interface ReactActionFunction<TContext, TEvent extends EventObject> {+  (+    context: TContext,+    event: TEvent,+    meta: ActionMeta<TContext, TEvent>+  ): () => void;+  __effect: ReactEffectType;+}++export interface ReactActionObject<TContext, TEvent extends EventObject>+  extends ActionObject<TContext, TEvent> {+  exec: ReactActionFunction<TContext, TEvent>;+}++export function asEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `effect:${exec.name}` },+    __effect: { value: ReactEffectType.Effect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export function asLayoutEffect<TContext, TEvent extends EventObject>(+  exec: ActionFunction<TContext, TEvent>+): ReactActionFunction<TContext, TEvent> {+  const effectExec: unknown = (context, event, meta) => {+    // don't execute; just return+    return () => {+      return exec(context, event, meta);+    };+  };++  Object.defineProperties(effectExec, {+    name: { value: `layoutEffect:${exec.name}` },+    __effect: { value: ReactEffectType.LayoutEffect }+  });++  return effectExec as ReactActionFunction<TContext, TEvent>;+}++export type ActionStateTuple<TContext, TEvent extends EventObject> = [+  ReactActionObject<TContext, TEvent>,+  State<TContext, TEvent>+];++function executeEffect<TContext, TEvent extends EventObject>(+  action: ReactActionObject<TContext, TEvent>,+  state: State<TContext, TEvent>+): void {+  const { exec } = action;+  const originalExec = exec!(state.context, state._event.data, {+    action,+    state: state,+    _event: state._event+  });++  originalExec();+}++interface UseMachineOptions<TContext, TEvent extends EventObject> {+  /**+   * If provided, will be merged with machine's `context`.+   */+  context?: Partial<TContext>;+  /**+   * The state to rehydrate the machine to. The machine will+   * start at this state instead of its `initialState`.+   */+  state?: StateConfig<TContext, TEvent>;+}++export function useMachine<+  TContext,+  TEvent extends EventObject,+  TTypestate extends Typestate<TContext> = any+>(+  machine: StateMachine<TContext, any, TEvent, TTypestate>,+  options: Partial<InterpreterOptions> &+    Partial<UseMachineOptions<TContext, TEvent>> &+    Partial<MachineOptions<TContext, TEvent>> = {}+): [+  State<TContext, TEvent, any, TTypestate>,+  Interpreter<TContext, any, TEvent, TTypestate>['send'],+  Interpreter<TContext, any, TEvent, TTypestate>+] {+  if (process.env.NODE_ENV !== 'production') {+    const [initialMachine] = useState(machine);++    if (machine !== initialMachine) {+      console.warn(+        'Machine given to `useMachine` has changed between renders. This is not supported and might lead to unexpected results.\n' ++          'Please make sure that you pass the same Machine as argument each time.'+      );+    }+  }++  const {+    context,+    guards,+    actions,+    activities,+    services,+    delays,+    state: rehydratedState,+    ...interpreterOptions+  } = options;++  const service = useConstant(() => {+    const machineConfig = {+      context,+      guards,+      actions,+      activities,+      services,+      delays+    };++    const createdMachine = machine.withConfig(machineConfig, {+      ...machine.context,+      ...context+    } as TContext);++    return interpret(createdMachine, interpreterOptions).start(+      rehydratedState ? State.create(rehydratedState) : undefined+    );+  });++  const [state, setState] = useState(service.state);++  const effectActionsRef = useRef<+    Array<[ReactActionObject<TContext, TEvent>, State<TContext, TEvent>]>+  >([]);+  const layoutEffectActionsRef = useRef<+    Array<[ReactActionObject<TContext, TEvent>, State<TContext, TEvent>]>+  >([]);++  useLayoutEffect(() => {+    service.onTransition((currentState) => {+      if (currentState.changed) {+        setState(currentState);+      }++      if (currentState.actions.length) {+        const reactEffectActions = currentState.actions.filter(+          (action): action is ReactActionObject<TContext, TEvent> => {+            return (+              typeof action.exec === 'function' &&+              '__effect' in (action as ReactActionObject<TContext, TEvent>).exec+            );+          }+        );++        const [effectActions, layoutEffectActions] = partition(+          reactEffectActions,+          (action): action is ReactActionObject<TContext, TEvent> => {+            return action.exec.__effect === ReactEffectType.Effect;+          }+        );++        effectActionsRef.current.push(+          ...effectActions.map<ActionStateTuple<TContext, TEvent>>(+            (effectAction) => [effectAction, currentState]+          )+        );++        layoutEffectActionsRef.current.push(+          ...layoutEffectActions.map<ActionStateTuple<TContext, TEvent>>(+            (layoutEffectAction) => [layoutEffectAction, currentState]+          )+        );+      }+    });++    // if service.state has not changed React should just bail out from this update+    setState(service.state);++    return () => {+      service.stop();+    };+  }, []);++  useLayoutEffect(() => {+    while (layoutEffectActionsRef.current.length) {+      const [+        layoutEffectAction,+        effectState+      ] = layoutEffectActionsRef.current.shift()!;++      executeEffect(layoutEffectAction, effectState);+    }+  }, [state]);++  useEffect(() => {+    while (effectActionsRef.current.length) {+      const [effectAction, effectState] = effectActionsRef.current.shift()!;++      executeEffect(effectAction, effectState);+    }+  }, [state]);++  // Make sure actions and services are kept updated when they change.+  // This mutation assignment is safe because the service instance is only used+  // in one place -- this hook's caller.+  useEffect(() => {

Updating those IMHO should happen before flushing queued actions - executed actions would have a chance to execute with the fresh closure state.

davidkpiano

comment created time in 3 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha 757355da9d9810b7855621b0093cf26e323d48cd

Remove mentioning `inputRef` prop from the README

view details

push time in 2 days

push eventatlassian/changesets

Mateusz Burzyński

commit sha 4c3a8243c0b1a147e071938bff5c4e6089088bc5

Create a simple tag for single-package projects

view details

push time in 2 days

PR opened atlassian/changesets

Reviewers
Create a simple tag for single-package projects

For single-package projects including package name in the created tag name is not necessary and actually also hurts discoverability as those tags don't pop up for some reason in the tags dropdown on GitHub.

The proposed tag matches npm version scheme.

If I get a preliminary approval for this then I'm going to add a changeset and fix tests.

+18 -14

0 comment

2 changed files

pr created time in 2 days

create barnchatlassian/changesets

branch : simpler-tag-for-single-package-repos

created branch time in 2 days

delete branch Andarist/react-textarea-autosize

delete branch : changeset-release/master

delete time in 2 days

push eventAndarist/react-textarea-autosize

github-actions[bot]

commit sha 6e76a1c3487d67dfabbb46446aa6828e4b1708fa

Version Packages (#267) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

view details

push time in 2 days

PR merged Andarist/react-textarea-autosize

Version Packages

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to master, this PR will be updated.

Releases

react-textarea-autosize@8.0.1

Patch Changes

  • 2307033 #266 Thanks @vlazh! - Fixed a regression with calculating too high height for textareas with box-sizing: border-box;.

  • 1d1bba2 #265 Thanks @SimenB! - Exported TextareaAutosizeProps type for convenience.

  • da960f4 Thanks @Andarist! - Fixed an issue with internal cache not being populated correctly when using cacheMeasurements prop.

+13 -19

0 comment

5 changed files

github-actions[bot]

pr closed time in 2 days

PR closed Andarist/react-textarea-autosize

fix: Check _ref is not null

Sometimes _ref is null and will raise an exception when passed into calculateNodeHeight.

+4 -0

5 comments

1 changed file

chdsbd

pr closed time in 2 days

pull request commentAndarist/react-textarea-autosize

fix: Check _ref is not null

I have recently published v8 which simplifies the code a little bit and I hope it gets rid of this problem. If you encounter it again with v8 - please let me know.

chdsbd

comment created time in 2 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha a539dd166a183366f0126284cad7fc839184d26b

Add sponsor button

view details

push time in 2 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha da960f46084f3b584506f3513b77958d5265fcad

Fixed an issue with internal cache not being populated correctly when using `cacheMeasurements` prop

view details

push time in 2 days

pull request commentAndarist/react-textarea-autosize

Fix calculatation of height with box sizing 'border-box'

Thanks! I've screwed up this when refactoring. I've applied a slightly different fix for this based on v7 implementation because your proposed fix would calculate a too high height for border-box textareas with border or padding. Thanks a lot about raising this issue and preparing a PR with a potential fix ❤️

vlazh

comment created time in 2 days

push eventAndarist/react-textarea-autosize

vlazh

commit sha 230703341e366ad861e3a24e20f1d9fd6f9ced47

Fix calculatation of height with box sizing 'border-box' (#266) * Fix calculatation of height with box sizing 'border-box' Remove additional adding paddingSize and border because already contains in height (via getContentHeight) * Tweak fix for too high height for textareas with `box-sizing: border-box;` Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>

view details

push time in 2 days

PR merged Andarist/react-textarea-autosize

Fix calculatation of height with box sizing 'border-box'

Remove additional adding paddingSize and border because already contains in height (via getContentHeight)

+8 -6

1 comment

2 changed files

vlazh

pr closed time in 2 days

push eventvlazh/react-textarea-autosize

Mateusz Burzyński

commit sha b808e1b23423c3b84a78f60dbf132d5d6f7325fe

Tweak fix for too high height for textareas with `box-sizing: border-box;`

view details

push time in 2 days

pull request commentAndarist/react-textarea-autosize

fix: export Props type

Thanks!

SimenB

comment created time in 2 days

push eventAndarist/react-textarea-autosize

Simen Bekkhus

commit sha 1d1bba23140a7948b34a1cb9678802c71744b0f4

Export TextareaAutosizeProps type (#265) * fix: export Props type * Rename Props to TextareaAutosizeProps Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>

view details

push time in 2 days

PR merged Andarist/react-textarea-autosize

fix: export Props type

We wrap this component at work, and wanna provide our own props based on the abse ones. Exporting it makes it easier to access (and matches what the package on DefinitelyTyped did)

(I made this edit in GH's UI, so a bit hard to add the changeset requested by the bot)

+7 -2

2 comments

2 changed files

SimenB

pr closed time in 2 days

push eventSimenB/react-textarea-autosize

Mateusz Burzyński

commit sha c74dd2c0ddfb85ac92afc009caaa4501713c3fec

Rename Props to TextareaAutosizeProps

view details

push time in 2 days

pull request commentAndarist/react-textarea-autosize

fix: export Props type

I suspect (although I'm not entirely sure) that the second link (the one mentioning being a maintainer) would work for you as well.

It's not a problem though - I will add a changeset in a minute and gonna rename this type to TextareaAutosizeProps.

SimenB

comment created time in 2 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha f8803ef1e32fd45cad6364a1262a578ee7f41736

Update URL references to the repository and examples site

view details

push time in 3 days

issue commentemotion-js/emotion

className interpolation alternative

Sorry, we don't plan to bring back support for this - truth to be told we don't believe using any descendant selectors is a good idea. It's sometimes convenient - that's for sure, but it makes for a tightly coupled styles which are harder to maintain over the long period of time.

And we are not alone with that. Let's take a look at a recent post by Facebook - https://engineering.fb.com/web/facebook-redesign/ . We can find this there:

We guarantee that the styles are applied in a stable order, and we don’t support CSS descendant selectors.

If you really need to target a descendant element with emotion the best you can do is to use data attributes to select them, but again - we don't recommend this.

DarkoKukovec

comment created time in 3 days

issue commentramda/ramda

Remove ES Module build with a source change

Well, there is no much harm in including those extensions there, BUT why you (@Pyrolistical) would like to consume those files using source files? Distributed es directory is pretty much equivalent and it already contains those extensions. While Ramda is not really using much of a build step this might change in the future (probably not though). Having a distinction between source and distributed files give maintainers freedom to structure source files however they want, including using custom Babel plugins, TS, or whatever else.

It would certainly be great to ensure Ramda works nicely with Deno. Then it can be added to Deno's third-party modules list, as lodash is.

This is an interesting argument - but from what I understand this requires no build step which is somewhat a bummer as mentioned by me above. Shouldn't be much of a problem for Ramda, but it doesn't seem like a good strategy for the entire ecosystem. For instance, any React-based library has to compile their source files and committing both sources and distributed files is also not ideal.

Pyrolistical

comment created time in 3 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha 719e5a6a78bb2871146db6fcb83c194f9d4c2812

use @changesets/changelog-github

view details

Mateusz Burzyński

commit sha a01b9ed16fa781a1d153a321928a73559da48151

Update size of the library in the README

view details

push time in 3 days

issue closedAndarist/react-textarea-autosize

onHeightChange not fired when change minHeight property in style

onHeightChange not fired when change minHeight property in style.

closed time in 3 days

DmitriyMaximov

issue commentAndarist/react-textarea-autosize

onHeightChange not fired when change minHeight property in style

I've dropped handling props.style.minHeight entirely. Instead I recommend just using minRows prop. If you have a compelling use case for using style prop to control this - please let me know by creating a new issue.

DmitriyMaximov

comment created time in 3 days

issue closedAndarist/react-textarea-autosize

Cant pass custom Ref from Functional component

So i'm using 'hooks'

function TaskColumn({ title, children }) {
  const taskFieldRef = useRef()

Then in rendrer i'm trying to pass my ref

<TextareaAutosize
              innerRef={taskFieldRef}
              className='AddTaskField'
              value={newTaskField.value}
              placeholder={newTaskField.config.label}
              onChange={event => fieldChangedHandler(event)}
            />

But its not working. Can you maybe explain why and how to do it with hooks?

closed time in 3 days

WhoIsDT

issue commentAndarist/react-textarea-autosize

Cant pass custom Ref from Functional component

I've released the new major version which uses forwardRef under the hood so you should be able to just use ref prop now without any problems.

WhoIsDT

comment created time in 3 days

issue closedAndarist/react-textarea-autosize

inputRef no longer working

I'm on version 7.0 of this library and using React 16.9.

Passing the inputRef prop to TextareaAutosize does not do anything and I receive this console error: "React does not recognize the inputRef prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase inputref instead. If you accidentally passed it from a parent component, remove it from the DOM element."

I'm not doing anything else interesting. <TextareaAutosize value="some text" onChange={this.handleChange} inputRef={ref => this.input = ref}/>

closed time in 3 days

dcporter44

issue commentAndarist/react-textarea-autosize

inputRef no longer working

I've released the new major version which uses forwardRef under the hood so you should be able to just use ref prop now.

dcporter44

comment created time in 3 days

issue closedAndarist/react-textarea-autosize

emit ts definitions

Reproduce:

// Using typescript:
const MyTextareaAutoSize = React.forwardRef<HTMLTextAreaElement>((props, ref) => {
    return (<TextareaAutoSize {...props} inputRef={ref} />);
})

The above code fails to compile because @types/react-textarea-autosize is old and does not declare support for null. Currently, I have to work around this with:

    return (<TextareaAutoSize {...props} inputRef={ref || undefined} />);

Since this is a TS project, could we package definition files into the release?

closed time in 3 days

dipunm

issue commentAndarist/react-textarea-autosize

emit ts definitions

Well - this package was not written in TS. I had an unpublished rewrite to TS available on master though 😅 and you have pushed me to finish the work and release v8: https://github.com/Andarist/react-textarea-autosize/releases/tag/react-textarea-autosize%408.0.0 . Enjoy 🎉

dipunm

comment created time in 3 days

delete branch Andarist/react-textarea-autosize

delete branch : changeset-release/master

delete time in 3 days

push eventAndarist/react-textarea-autosize

github-actions[bot]

commit sha 18756907cb0df5a8fe2b5edb6cb16a6314abb7b9

Version Packages (#264) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

view details

push time in 3 days

PR merged Andarist/react-textarea-autosize

Version Packages

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to master, this PR will be updated.

Releases

react-textarea-autosize@8.0.0

Major Changes

  • The package has been rewritten in TypeScript so type definitions are now included in the package itself. There is no need to install separate types from the DefinitelyTyped.
  • At the same time the package internals have been rewritten to use React's hooks API. This means that the peer dependency requirement for React version had to be changed to ^16.8.0.
  • You can now use ref prop to get access to the underlaying textarea element as React.forwardRef is being used now. The support for innerRef has been completely removed.
  • useCacheForDOMMeasurements prop has been renamed to cacheMeasurements.
  • onHeightChange callback no longer receives the second argument. It was the component's instance (its this), but as the component is now implemented using hooks there no longer is any instance that could be given to a consumer like that.
  • Removed handling props.style.maxHeight and props.style.minHeight values. If you need to control those boundaries you should use maxRows and minRows props respectively.

Minor Changes

  • The height is being set now directly on the underlaying textarea element and not caused by updating internal state and this triggering React's rerender. This shouldn't make for any observable difference for consumers of this package.
+17 -39

0 comment

9 changed files

github-actions[bot]

pr closed time in 3 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha 3108479594e6319d69e97d60e86115f5f861a5d2

Add last changeset about removing support for style.minHeight/maxHeight

view details

push time in 3 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha d6916047950e559124645a2c78b0b40ab9607e78

Fix how `@changesets/cli/changelog` got required

view details

push time in 3 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha ddb8a825d3127266df55a6cca257e15d57108296

Add temporarily custom changelog generator

view details

push time in 3 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha 22abf32f8545e8925f4cbabe1d3d66a34cbabd4e

Reorder changesets

view details

push time in 3 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha 6885c685568e4e608eab31f29ced0fa817964c25

Switch to GitHub actions + prepare release workflow

view details

push time in 3 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha 767df78199aaa3f5c1b53295141774ff4697feba

Add v8 changesets (#263)

view details

push time in 3 days

delete branch Andarist/react-textarea-autosize

delete branch : v8-changesets

delete time in 3 days

create barnchAndarist/react-textarea-autosize

branch : v8-changesets

created branch time in 3 days

push eventAndarist/react-textarea-autosize

Mateusz Burzyński

commit sha 0fbdf357507a28d70e0349b6ea0d56db502ba69c

Update testing deps

view details

Mateusz Burzyński

commit sha 7241f84c8df41a3a518049d659dd7f92129ac10c

"Fix" tests

view details

Mateusz Burzyński

commit sha cf4f177f678eb8bbad65f70cca4ce054c36e2766

Disable @typescript-eslint/no-non-null-assertion rule

view details

push time in 3 days

pull request commentandreypopp/react-textarea-autosize

Proposal: Allow callers to avoid subpixel height values

Hm, couldn't you work around this by tweaking your styles? It would be much better if you just wouldn't run into this at all, rather than applying a fixup on the computed value.

andrewkshim

comment created time in 4 days

PR closed andreypopp/react-textarea-autosize

Link BundlePhobia

https://bundlephobia.com/result?p=react-textarea-autosize

+1 -1

1 comment

1 changed file

oliviertassinari

pr closed time in 4 days

pull request commentandreypopp/react-textarea-autosize

Link BundlePhobia

The reason why I'm not linking to bundlephobia is that I don't believe their metric is accurate. It's not that the value given in README here is taken out of thin air or that it's outdated - it actually comes from a measuring script: https://github.com/andreypopp/react-textarea-autosize/blob/2cd8abd82a76d3b97b17bc80c2350133b2828932/package.json#L34

oliviertassinari

comment created time in 4 days

issue commentdavidkpiano/xstate

Unable to pass invoked machines (children) down to child components in React

The problem you are having is that you have 2 separate states in which you have a different service invoked but you render 2 components (each wanting to access one of those invoked services) unconditionally. That it doesnt work seems quite natural to me as you implicitly create a bound between a component and a service so how could component render OK without its counterpart being live?

Keep in mind that invoked services stay active only when a machine stays within containing state. If you want to have manual control over their lifetime then you can use spawn instead of invoke.

Blacktiger

comment created time in 4 days

issue commentredux-saga/redux-saga

put or putResolve no difference?

No - putResolve just works with thunk actions that return promises, so it can block until that promise resolves. It has no effect on regular actions.

alanhg

comment created time in 4 days

issue closeddavidkpiano/xstate

Parent is not sent on spawned machines

Description I'm unable to use sendParent from a spawned machine to send an event back to the machine that spawned it. Something that would be nice is if the sendParent action could automatically forward an event from the child as well rather than having to write out the event completely. (ie { on: { CREATE: sendParent() }} would just send the whole event automatically)

Expected Result The spawned machine should be able to use sendParent to send events back to the parent machine.

Actual Result Nothing happens. Adding service.onEvent(console.log) shows this message "Event "CREATE" was sent to stopped service "(machine)". This service has already reached its final state, and will not transition. Event: {"type":"CREATE"}"

Reproduction https://codesandbox.io/s/spawn-test-554os

closed time in 4 days

Blacktiger

issue commentdavidkpiano/xstate

Parent is not sent on spawned machines

Duplicate of #1125

The issue got explained here: https://github.com/davidkpiano/xstate/issues/1125#issuecomment-613425210 . We plan to fix it soon - although in a slightly different manner than mentioned there. This PR is related to the planned fix: https://github.com/davidkpiano/xstate/pull/1202

Blacktiger

comment created time in 4 days

issue commentdavidkpiano/xstate

Parent is not sent on spawned machines

Just want to let you know that I'm investigating this issue.

Blacktiger

comment created time in 4 days

delete branch thysultan/stylis.js

delete branch : fix/double-hyphen-in-identifier

delete time in 4 days

push eventthysultan/stylis.js

Mateusz Burzyński

commit sha 7e96900e149a8db91a43d956cf21ed3213196056

Fix an issue with double hyphen in an identifier switching to parsing css var declaration (#221)

view details

push time in 4 days

PR merged thysultan/stylis.js

Fix an issue with double hyphen in an identifier switching to parsing css var declaration

fixes problem discovered when investigating https://github.com/thysultan/stylis.js/issues/220

+17 -1

1 comment

2 changed files

Andarist

pr closed time in 4 days

issue commentdavidkpiano/xstate

NodeJs memory leak with interpret (xstate>=4.7.0-rc6)

Thank you for sharing the repro case i will take a look at this soon!

schemelev

comment created time in 4 days

issue closedthysultan/stylis.js

Syntax error in deep nesting

Hi, I have detected in the received syntax as output the following example:

Input :

.component{
	&.--state-a{	
		color:black;
		&.--state-a{	
			color:black;
		}
	}
}

Output received:

[namespace] .component {
&.--state-a {
	color: black;
&.--state-a {
	color: black;
}

Expected output:

[namespace] .component.--state-a {
	color: black;
}

[namespace] .component.--state-a.--state-a {
	color: black;
}

I'm reading the code, to be able to contribute to stylis.

closed time in 4 days

UpperCod
more