Hacker Newsnew | past | comments | ask | show | jobs | submit | jamesisaac's commentslogin

You might find this recent blog post from the React Native team informative, about their long-term vision for multi-platform support: https://reactnative.dev/blog/2021/08/26/many-platform-vision

From personal experience, I've had great success targeting the 3 platforms you mention in a single RN codebase, with a very high degree of code re-use.


This isn't really accurate. Presuming you're using react-native-web [1] (the most popular version of RN targeting the web, although there are others), it's essentially just a set of components and APIs, fulfilling RN's API, which sit on top of React DOM.

So anything you could do in React, you can also do in RNWeb, as it's a superset of React.

Of course, you'd need to use the RN components if you want to share code between web and native mobile. But there's nothing stopping you reaching a high degree of code re-use, and/or using React components for the web-only portion of a RNWeb project.

[1] https://github.com/necolas/react-native-web


SEEKING WORK | Remote (London, UK) | Full-Stack Web & Mobile App, Design & Development

Portfolio: https://www.m10c.com/portfolio

Email: james@m10c.com

I've been designing and building apps and web platforms for the last 10+ years, currently freelancing with a small team. Lots of experience helping startups launch future-proof MVPs, and happy to offer any level of assistance across the whole product spectrum (from UX, to marketing, to business strategy..).

Frontend focus is React/React Native, as it's allowed us to target all 3 platforms (web, iOS, Android) with unprecedented efficiency.


Something which I think could be helpful for the overall ecosystem, and allow both type systems to coexist and thrive, would be more attention put into tools which transpile code and type definitions between the two languages. The two most popular projects that I could find[1][2] are unfortunately incomplete and have not received any real updates in months.

[1] https://github.com/joarwilk/flowgen [2] https://github.com/bcherny/flow-to-typescript


So, I was one of (or I suppose the only...) person in the linked issue arguing that competition is useful, and that it makes sense for Facebook's products to be tied into the same ecosystem.

As others have mentioned, when Flow was first launched, TS had an extremely limited type system (e.g. no null checking, no union types). Of course this has improved a lot over time, as the two have converged, but there's still a long way to go for either project to allow JS reach the level of correctness and expressiveness of, say, OCaml or Haskell.

Flow has consistently for the past few years been bringing new ideas to the table from this algebraic data types background, which often have ended up proving to be good ideas, and are in various stages of trickling down to TS.

I worry that if the ecosystem becomes completely dominated by TS, the overall focus will end up back where TS started: rudimentary OOP inspired types, without the underlying goal of overall correctness, which Flow strives for, while TS openly eschews in favour of "pragmatism".


You can’t have the same level of correctness as ocaml if you want to maintain the compatibility with JavaScript. Typescript type system is unsound for this reason. You can’t have anything approaching ocaml correctness when in typescript all objects with the same shape are interchangeable.


Yeah I realise you'd never reach the same level of soundness due to the limitations of the underlying JS (although presumably you could get close with a subset?). But that's why it's an interesting challenge, and I think what Flow has shown is that it's possible to get a lot closer than had previously been imagined.

If everyone just accepted the argument 4 years ago that "JS will never be sound" then maybe today TS would still just be Java style `interface` annotations for classes. It's not like the Flow team has reached a ceiling at this point... there's still plenty on their roadmap that would continue to improve soundness and expressiveness.

> You can’t have anything approaching ocaml correctness when in typescript all objects with the same shape are interchangeable.

Could you elaborate? Flow has recently switched to exact objects by default[1], which I would have thought would be enough for a sound approach?

[1] https://medium.com/flow-type/on-the-roadmap-exact-objects-by...


For example in typescript this is valid:

    type A = {name: string}
    type B = {name: string}
    function print(obj: A) { alert(obj.name);}
    let a: A = {name: “hello”};
    let b: B = {name: “world”}
    print(a);
    print(b);
This is because of typescript structural equality and I think that the same applies to flow given your link. Obviously if I want a function to accept an email I don’t want the same function to accept an address, but in typescript you can’t guarantee it because you have no way to get rid of structural equality as far as I understood.


Ah I see. That can be achieved since Flow 0.51 with opaque types[1][2]. It seems like TypeScript hasn't yet caught up with this functionality[3].

[1] https://medium.com/flow-type/hiding-implementation-details-w...

[2] https://flow.org/en/docs/types/opaque-types/

[3] https://github.com/Microsoft/TypeScript/issues/15807#issueco...


Yup that seems to help.


I'm certainly not well versed in OCaml, but my understanding is that it is also structurally typed. This particular example would also be permitted in OCaml, correct?


No, records are nominally typed in OCaml, thus the two types `A` and `B` would be deemed incompatible.


But objects in OCaml are structurally typed (well, row typed).


This is currently a stage 1 proposal for the core language: https://github.com/tc39/proposal-optional-chaining

Depending on your stack it might be possible to make use of it already, e.g. Babel has a transpilation plugin, and Flow supports it for static type-checking.


Why use componentWillMount and not componentDidMount? From the docs:

> componentWillMount() is invoked immediately before mounting occurs. It is called before render(), therefore setting state synchronously in this method will not trigger a re-rendering. Avoid introducing any side-effects or subscriptions in this method.

> componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.


My point isn't so much loading data from a remote endpoint, but setting state. As the doc you quote says: componentWillMount should not trigger rerendering, and should have the newest state in the render function.

But when you use Redux this isn't true! The first render will see the old state of the store even if you did changes to it right befofe. And then you immediately get a new rerender with the correct state. This cannot be fixed with how react-redux is currently implemented, and leads to subtle bugs, especially when unmounting and remounting stuff that tries to clear some state.

Edit: more details https://github.com/reactjs/react-redux/issues/210


As Dan said when closing that issue, this seems to me how I would expect rendering in React to work... it's async by nature.

If you need the component's state/props to be a certain way for the very first render, use `this.state = {...}` in the contructor, or wait until the Redux store is in the correct shape before mounting the component. (I'm guessing this is what you've done in your HOC, so this sounds like the right solution to me).

Calls like `dispatch` and `setState` shouldn't be expected to reflect in a component's rendering synchronously. And as far as I know, as of Fiber, componentWillMount is even going to become a method that could be called multiple times before the component actually mounts... its usage is pretty widely discouraged for most cases: https://github.com/facebook/react/issues/7671


> Option 1: Use a string or null/false data [...] If you're using a type checker like #flow, it will yell at you and for a good reason: data type changes are hard to reason about and very error prone in practice.

This isn't true. Flow is great at enforcing that all potential types/values are handled in an if statement. If the following type was used, the code missing the failure case would have been picked up by Flow:

    type Pics = 'fetching' | 'failed' | Array<Pic>
> Option 2: use an object with two props [...] someone accidentally changes the status without changing the data. Or the other way around. This will result in nasty and hard to debug issues where the state and data don't match visually.

Again, this can be avoided by using the correct union in Flow, which will pick up right away if the two properties have become inconsistent:

    type PicsState =
      | { status: 'fetching', list: null }
      | { status: 'failed', list: null }
      | { status: 'fetched', list: Array<Pic> }


Hadn't seen the union approach in #2 before, I like that a lot. The 2 named properties are intuitive to other developers reading it, but now we also get compile-time safety that the combinations they use are valid. Thanks!

For anyone else that this was new to, Flow has documented these as "disjoint unions": https://flow.org/en/docs/types/unions/#toc-disjoint-unions


I'd highly recommend using a type system like Flow to enumerate the shape of possible actions the store should support. This brings numerous advantages:

1. You can use strings for action types, no more need to import constants.

2. No need for action creators for simple actions, you can just create the action object inline and Flow will validate its shape.

3. You can be sure that reducers are unpacking properties that actually exist from the actions.

In other words, your example could simply be:

    // types.js
    type Action =
      | { type: 'ADD_TODO', text: string }

    // TodoList.js
    dispatch({ type: 'ADD_TODO', text })
...and Flow would confirm at 'compile time' that it matches the contract dispatch expects.

This approach has scaled up extremely well for me and no longer feels verbose.


I agree with that. When I first started with Redux and Flow, I was just following all the examples and made sure I used action creators for everything, even if there was no payload. Now I'm feeling more confident with Flux, and I recently realized that no, I don't need "action creators" for everything. That's silly. It's OK to just dispatch an object using your action constant directly.

And then I found myself writing action creators on purpose, not because I saw it in some tutorial, but because it actually does remove some duplication.

This was also missing from my education for a long time: https://github.com/acdlite/flux-standard-action#actions

I had no idea why I was using a "payload" key, instead of just putting all the values in the top-level object. But this convention makes a lot of sense now.


Maybe what some people are looking for in a replacement, this is something I put together:

https://nachapp.com

It isn't multi-user, but does introduce features around tying tasks to larger goals, progress tracking etc. It's also currently just a web app, but at the moment I'm working on native iOS/Android apps.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: