Giter VIP home page Giter VIP logo

Comments (20)

loganpowell avatar loganpowell commented on May 13, 2024

A link to my code:

https://github.com/loganpowell/RAATS/blob/master/src/index.js#L25

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

Hold on... I think I might have zero'd in on it!

from curi.

pshrmn avatar pshrmn commented on May 13, 2024

Just for posterity, the issue is that you are using pathnames in your <Link>s when all you really need to use are the route's names.

-<Link to="/">Home</Link>
+<Link to="Home">Home</Link>
-<Link to="/subpage">Subpage</Link>
+<Link to="Subpage">Subpage</Link>
-<Link to="/courses">Courses</Link>
+<Link to="Courses">Courses</Link>

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

Aha! That was it. I was way off

from curi.

pshrmn avatar pshrmn commented on May 13, 2024

Sometimes its more characters (Home vs /), but I really like not having to care about pathnames in my code.

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

For my sake (and for other beginners), why have you chosen to pack the app under a render function instead of providing a <Provider> API like Apollo, RR, Styled-Components, etc...

from curi.

pshrmn avatar pshrmn commented on May 13, 2024

Do you mean why this

<CuriBase ... render={render} />

and not this?

<CuriBase ...>
  <App />
</CuriBase>

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

Yes... I'm looking at the Redux example and it feels like the Curi API is in a number of locations (I was hoping for a "centralized" experience ;). I'm sorry if it's less complex than it looks to me

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

I'm just trying to help

from curi.

pshrmn avatar pshrmn commented on May 13, 2024

There are a few reasons, although it might just come down to being personal preference.

The <CuriBase> is essentially the same as a <Provider>, but with a different name. It has two functions:

  1. To call its render prop function whenever it re-renders, which in turn will re-render the application. This could be replaced with children, but I'll get into why I prefer the render prop.
  2. To make variables accessible through React's context so that you don't have to manually pass the response, router, and navigation down through every component. React is actually changing how the context works, so this might change soon. Since Curi really hasn't picked up steam yet (and it's still in beta), I might look into switching over to only the new method, but I'm not sure about the details of that yet and what changes @curi/react might need to implement that.

So why a render prop? The root child of the <CuriBase> is where an app "starts", and so it needs the response object in order to know what to render. Using a render prop (it doesn't always have to be under the render prop, children is what React's new context will use) allows you to explicitly inject the response (and the navigation and router if you would like) into this root.

<CuriBase ... render={(response, navigation) => {
  // every time the <CuriBase> re-renders, this will be called
  // with the new response/navigation objects
  return <App response={response} />;
}} />

To get the same functionality by passing a child element to <CuriBase>, you would either have to pass these props yourself, the <CuriBase> would internally have to use React.cloneElement to inject those props, or you would have to wrap the root in a <Curious>.

// explicitly pass props
router.respond((response, navigation) => {
  ReactDOM.render((
    <CuriBase ...>
      <App response={response}/>
    </CuriBase>
  ), holder);
});
// implicitly pass props
class CuriBase extends React.Component {
  render() {
    const { router, response, navigation, children } = this.props;
    // need to make sure there is only one child element
    const child = React.Children.only(children)
    return React.cloneElement(child, { router, response, navigation });
  }
}
<CuriBase ...>
  <App /> {/* router, response, and navigation will be injected */}
</CuriBase>
// use Curious to inject props, which uses a render prop like the current CuriBase
<CuriBase ...>
  <Curious render={curiProps => <App {...curiProps} />} />
</CuriBase>

Just for a comparision, in React Router the root component doesn't actually have access to routing props, so if you need them there, you end up having to wrap that component in a <Route>.

// if you just render an App inside a Router,
// it doesn't get the history, match, or location
<BrowserRouter>
  <App />
</BrowserRouter>
// you have to use withRouter or Route to inject those
<BrowserRouter>
  <Route component={App} />
</BrowserRouter>

const withApp = withRouter(App)
<BrowserRouter>
  <App />
</BrowserRouter>

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

Damn man. You make me feel bad when you put this much into responding to my naive questions! Thank you. I guess it will just take some getting used to.

πŸ™

from curi.

pshrmn avatar pshrmn commented on May 13, 2024

If it makes more sense to you, just inline the render function.

<CuriBase
  ...
  render={response => (
    <div>
      <response.body response={response} />
    </div>
  )}
/>

Using a separate function is only minimally more efficient (with an inline function, a new function is created every render) but might make things more difficult to keep track of. The biggest benefit of a separate render function for me is that you don't have a bunch of imports at the top of your index.js file.

Do you think that the tutorial would be more straightforward with an inline render prop? I have no problem switching to that.

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

So, please bear with me as I don't exactly know how stupid my questions are and therefore assume they are all either stupid or misplaced (forgive me, with all respect).

How difficult would it be for me to 'mimic' the API of RR? Just so I could get going with Curi in a "drop-in" like manner? I figure some people will want to give this a try (like me) with an existing setup and butt-load of libraries (Apollo, Redux, etc.) adding a route.js config of course, in a "Migrating from RR" or something like that - tutorial. Just so folks can build out the mental model of Curi's API without having to start with a tabula rasa - unless "we must unlearn what we have learned", which would also be fine if it's necessary. Just kinda spit-balling here, please don't assume I'm your average customer. I'm a super newb!

How inefficient is this:

class CuriBase extends React.Component {
  render() {
    const { router, response, navigation, children } = this.props;
    // need to make sure there is only one child element
    const child = React.Children.only(children)
    return React.cloneElement(child, { router, response, navigation });
  }
}

and then:

class App extends Component {
  render() {
    return (
      <CuriBase>
        <ApolloProvider client={client}>
          <ThemeProvider theme={censusMineral}>
            <div className="App">
              <div className="TopBar">
                <Link to="Home">Home</Link>
                <Link to="Subpage">Subpage</Link>
                <Link to="Courses">Courses</Link>
              </div>
              <AnimatedRoutes>
                <BodyComponent.../>
              </AnimatedRoutes>
            </div>
          </ThemeProvider>
        </ApolloProvider>
      </CuriBase>
    );
  }
}

export default App;

I guess I'm kinda wondering if there are any ways I can shrink the API and kinda forget about it, ykwim?

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

Edit: I erased this and didn't paste it back in:

I think there could be a "Beginners" path and then, maybe more advanced topics. To be completely fair, your tutorial is very straight forward! I am just trying to memorize the API, which will always take time.

from curi.

pshrmn avatar pshrmn commented on May 13, 2024

There isn't anything inefficient with cloneElement (it is just making a copy of an object than adding some additional properties to it). My main concern is the "magic" involved there. I like to see the props getting passed to a component.

As far as other providers go, I would recommend wrapping those around the <CuriBase>.

<ApolloProvider client={client}>
  <ThemeProvider theme={censusMineral}>
    <CuriBase
      router={router}
      response={response}
      navigation={navigation}
      render={response => (
        <div className="App">
          <div className="TopBar">
            <Link to="Home">Home</Link>
            <Link to="Subpage">Subpage</Link>
            <Link to="Courses">Courses</Link>
          </div>
          <AnimatedRoutes>
            <response.body .../>
          </AnimatedRoutes>
        </div>
      )}
    />
  </ThemeProvider>
</ApolloProvider>

I think that I'm going to add a <ResponsiveBase> component to the @curi/react and @curi/react-native packages. That can automatically re-render when a new response is emitted (similar to wrapping <CuriBase> in a connect for Redux users).

// { once: true } means that this response handler will only be called
// once and the <ResponsiveBase> will take care of re-rendering
router.respond(() => {
  ReactDOM.render((
    <ApolloProvider client={client}>
      <ThemeProvider theme={censusMineral}>
        <ResponsiveBase router={router} render={...} />
      </ThemeProvider>
    </ApolloProvider>
  ), holder)
}, { once: true });

I should also write a migrating from RRv4 guide. RRv4 is really the odd one out as far as routers go; most other routers are similar to Curi.

Another change that I think should be made are the arguments passed to the <CuriBase render>. Right now, it receives three arguments: response, navigation, and router. However, I think those should be grouped under a single object. Then, the render prop would effectively be passed a stateless React component.

const App = ({ response }) => (
  <response.body />
);

<CuriBase ... render={App} />

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

Right now, it receives three arguments: response, navigation, and router. However, I think those should be grouped under a single object.
I like that a lot!

There's still this kinda "hairy elbow" though:

router.respond(() => {
  ReactDOM.render((
    <ApolloProvider client={client}>
      <ThemeProvider theme={censusMineral}>
        <ResponsiveBase router={router} render={...} />
      </ThemeProvider>
    </ApolloProvider>
  ), holder)
}, { once: true });

This kinda makes me feel like Curi is taking over my app. Could it take some cues from the cross-platform Apollo Client?

import curi, { CuriContext } from "@curi/core";
import { CuriBase } from "@curi/react";
import routes from "./routes";
import renderFunction from "./render";
const history = Browser();
const router = curi(history, routes);

const context = new CuriContextt({
  router={router}
  response={response}
  navigation={navigation}
});

ReactDOM.render(
  <CuriBase context={context}>
    <MyAppComponent />
  </CuriBase>,
  document.getElementById('root')
)

I don't know how much of your API has been designed around the VDOM-agnosticism, so - again - forgive me if this is an important part of the implementation.

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

I've been abandoning my family for a few days, so I have to peel away from the computer tonight, but I'll be back manjana!

πŸ‡ΊπŸ‡Έ

from curi.

pshrmn avatar pshrmn commented on May 13, 2024

I appreciate the time you have taken and the feedback that you have given, but family is much more important. Also, while I (with bias) think that Curi is better than React Router, I also realize that RR is nearly ubiquitous with React devs, so please don't feel like you need to use Curi over RR. I hope it really grows and becomes a viable alternative, but there is a long way to go.

Once you do get back, as far as "Curi is taking over my app" goes, is it the router.respond that you're concerned with? The reason that that exists is to delay rendering until a response has been created. You can skip that, but the problem is that you have to figure out what to render before the first response is emitted (in React Native, where you cannot wait for the initial response, I have just used a <Text>Loading...</Text>).

const router = curi(history, routes);

ReactDOM.render((
  <ResponsiveBase router={router} render={({ response }) => {
    if (!response) { return <div>Loading...</div>; }
    return (
      <div>
        <response.body response={response} />
      </div>
    );
  }} />
), holder);

#9 adds a synchronous mode to Curi, which would mean you don't have to wait for the initial response, but it also means that you cannot use either match.initial or match.every (those are asynchronous, so Curi has to wait for those to finish before it can emit a response). I'm still debating whether or not to merge that, but I probably will.

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

from curi.

loganpowell avatar loganpowell commented on May 13, 2024

I bet you were like That's not how observables work

from curi.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.