React router #343

Merged
bones7242 merged 96 commits from react-router into master 2018-02-15 08:02:17 +01:00
bones7242 commented 2018-02-05 05:35:43 +01:00 (Migrated from github.com)
  • added react-router
  • rewrote relevant handlebars templates/partials as react components
  • updated api routes to serve react
- added react-router - rewrote relevant handlebars templates/partials as react components - updated api routes to serve react
neb-b (Migrated from github.com) requested changes 2018-02-05 22:53:41 +01:00
neb-b (Migrated from github.com) left a comment

Initial comments. I'll do another look through later. Looking great though!

Initial comments. I'll do another look through later. Looking great though!
neb-b (Migrated from github.com) commented 2018-02-05 20:57:15 +01:00

I think generally the pattern is that an action is { type: "some string", data: { name, id... } } just to keep things consistent. data can be an object or a string, but I think it's helpful to put everything inside of that

I think generally the pattern is that an action is `{ type: "some string", data: { name, id... } }` just to keep things consistent. `data` can be an object or a string, but I think it's helpful to put everything inside of that
neb-b (Migrated from github.com) commented 2018-02-05 20:47:37 +01:00

Why do you do const that = this?

Why do you do `const that = this`?
neb-b (Migrated from github.com) commented 2018-02-05 20:52:52 +01:00

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same <AssetDisplay /> it will make these requests again, even if you just made them a second ago

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same `<AssetDisplay />` it will make these requests again, even if you just made them a second ago
neb-b (Migrated from github.com) commented 2018-02-05 20:39:44 +01:00

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b (Migrated from github.com) commented 2018-02-05 20:38:50 +01:00

Any reason these aren't just css?

Any reason these aren't just css?
neb-b (Migrated from github.com) commented 2018-02-05 20:34:53 +01:00

You can use destructuring twice to avoid all the repeated this.props.claimData

const { claimData: { name, claimId... } } = this.props

Then you can just use name={name}

You can use destructuring twice to avoid all the repeated `this.props.claimData` `const { claimData: { name, claimId... } } = this.props` Then you can just use `name={name}`
neb-b (Migrated from github.com) commented 2018-02-05 20:27:54 +01:00

Will these nested values always exist?

Will these nested values always exist?
neb-b (Migrated from github.com) commented 2018-02-05 20:26:45 +01:00

This might be what you were thinking of doing, but instead of doing the request here, then calling an action to update the data or set an error, just dispatch updateClaimsData action which makes the call, then updates the redux state accordingly. I think making an effort to keep all data logic inside of redux files can simplify a lot of components (for the most part)

This might be what you were thinking of doing, but instead of doing the request here, then calling an action to update the data or set an error, just dispatch `updateClaimsData` action which makes the call, then updates the redux state accordingly. I think making an effort to keep all data logic inside of redux files can simplify a lot of components (for the most part)
neb-b (Migrated from github.com) commented 2018-02-05 20:18:47 +01:00

Instead of having a activeClassName prop, it might be simpler to use an active prop. Then you can just change the active class name once inside the NavLink component

Instead of having a `activeClassName` prop, it might be simpler to use an `active` prop. Then you can just change the active class name once inside the `NavLink` component
@ -121,3 +114,3 @@
.then(() => {
return that.validatePublishParams();
return this.validatePublishParams();
})
neb-b (Migrated from github.com) commented 2018-02-05 20:15:43 +01:00

Not really a comment on this PR but it might be worth looking into fetch instead of dealing with xhr

Not really a comment on this PR but it might be worth looking into `fetch` instead of dealing with `xhr`
neb-b (Migrated from github.com) commented 2018-02-05 20:12:41 +01:00

In the app we use a util to avoid a lot of the boiler plate with redux.
https://github.com/lbryio/lbry-app/blob/master/src/renderer/util/redux-utils.js

It just makes it so you don't need to use a switch. I really like it.

In the app we use a util to avoid a lot of the boiler plate with redux. https://github.com/lbryio/lbry-app/blob/master/src/renderer/util/redux-utils.js It just makes it so you don't need to use a switch. I really like it.
neb-b (Migrated from github.com) commented 2018-02-05 20:10:19 +01:00

Probably worth adding a 404 page or just redirect to the homepage if it doesn't find a route. Either with a Redirect component or just a route with no path prop.

Probably worth adding a 404 page or just redirect to the homepage if it doesn't find a route. Either with a `Redirect` component or just a route with no `path` prop.
@ -4,3 +2,4 @@
<div id="react-app" class="row row--tall flex-container--column">
<div class="row row--padded row--tall flex-container--column flex-container--center-center">
<p>loading...</p>
{{> progressBar}}
neb-b (Migrated from github.com) commented 2018-02-05 20:14:26 +01:00

Not sure if you meant to keep this in? I think this will always be rendered.

Not sure if you meant to keep this in? I think this will always be rendered.
bones7242 (Migrated from github.com) reviewed 2018-02-07 00:13:24 +01:00
bones7242 (Migrated from github.com) commented 2018-02-07 00:13:24 +01:00

I had a misunderstanding of how the this context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.

I had a misunderstanding of how the `this` context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.
bones7242 (Migrated from github.com) reviewed 2018-02-07 07:28:57 +01:00
bones7242 (Migrated from github.com) commented 2018-02-07 07:28:57 +01:00

oops

oops
bones7242 (Migrated from github.com) reviewed 2018-02-07 07:58:43 +01:00
bones7242 (Migrated from github.com) commented 2018-02-07 07:58:43 +01:00

They will always exist when ChannelClaimsDisplay is rendered, unless that should change... I am updated it to be destructured, is that what you were thinking?

They will always exist when ChannelClaimsDisplay is rendered, unless that should change... I am updated it to be destructured, is that what you were thinking?
neb-b (Migrated from github.com) reviewed 2018-02-07 08:10:06 +01:00
neb-b (Migrated from github.com) commented 2018-02-07 08:10:06 +01:00

I was just wondering if there would ever be a case when show is undefined. Which would cause an errror. cannot read property 'showChannel of undefined`.

Or if any of those children would be undefined which would throw an error

I was just wondering if there would ever be a case when `show` is undefined. Which would cause an errror. `cannot read property 'showChannel` of undefined`. Or if any of those children would be undefined which would throw an error
bones7242 (Migrated from github.com) reviewed 2018-02-09 20:19:28 +01:00
@ -4,3 +2,4 @@
<div id="react-app" class="row row--tall flex-container--column">
<div class="row row--padded row--tall flex-container--column flex-container--center-center">
<p>loading...</p>
{{> progressBar}}
bones7242 (Migrated from github.com) commented 2018-02-09 20:19:28 +01:00

I meant to keep the loading bar in so it will render until the app is rendered. Once the app renders it replaces the content of the div and removes the loading bar.

I meant to keep the loading bar in so it will render until the app is rendered. Once the app renders it replaces the content of the div and removes the loading bar.
bones7242 (Migrated from github.com) reviewed 2018-02-09 20:21:03 +01:00
@ -121,3 +114,3 @@
.then(() => {
return that.validatePublishParams();
return this.validatePublishParams();
})
bones7242 (Migrated from github.com) commented 2018-02-09 20:21:03 +01:00

Cool. Yeah, I replaced most xhr requests with fetch in the app. I kept this one for the publish request because I wanted to set event listeners on the request and wasn't sure if I could do that with fetch. I'll look into that.

Cool. Yeah, I replaced most xhr requests with fetch in the app. I kept this one for the publish request because I wanted to set event listeners on the request and wasn't sure if I could do that with fetch. I'll look into that.
bones7242 (Migrated from github.com) reviewed 2018-02-09 20:25:45 +01:00
bones7242 (Migrated from github.com) commented 2018-02-09 20:25:45 +01:00

I'm not quite sure I understand. Do you mean create my own NavLink component ( rather than using the NavLink from react-router-dom) and control the className from inside the component?

I'm not quite sure I understand. Do you mean create my own `NavLink` component ( rather than using the `NavLink` from `react-router-dom`) and control the className from inside the component?
bones7242 (Migrated from github.com) reviewed 2018-02-09 20:29:01 +01:00
bones7242 (Migrated from github.com) commented 2018-02-09 20:29:01 +01:00

Hmm, I like the readability of the switch statement, but I might use this util instead. I have to look at the app and see exactly how it works.

Hmm, I like the readability of the switch statement, but I might use this util instead. I have to look at the app and see exactly how it works.
neb-b (Migrated from github.com) reviewed 2018-02-09 20:57:10 +01:00
neb-b (Migrated from github.com) commented 2018-02-09 20:57:10 +01:00
Here is an example of it in the app https://github.com/lbryio/lbry-app/blob/master/src/renderer/redux/reducers/shape_shift.js#L99
neb-b (Migrated from github.com) reviewed 2018-02-09 21:17:09 +01:00
neb-b (Migrated from github.com) commented 2018-02-09 21:17:08 +01:00

Oh I didn't realize that was a react-router-dom component. But yeah, I would make a custom NavLink component that uses the react-router-dom component. Then you can extend the functionality/style very easily.

Oh I didn't realize that was a `react-router-dom` component. But yeah, I would make a custom `NavLink` component that uses the `react-router-dom` component. Then you can extend the functionality/style very easily.
neb-b (Migrated from github.com) reviewed 2018-02-09 21:17:28 +01:00
neb-b (Migrated from github.com) commented 2018-02-09 21:17:28 +01:00
similar to this https://github.com/lbryio/lbry-app/blob/redesign-wip/src/renderer/component/link/view.jsx
neb-b (Migrated from github.com) requested changes 2018-02-12 20:04:19 +01:00
neb-b (Migrated from github.com) left a comment

Looks good. Just some concerns with how you are bringing in some of the already fetched data. I think that can be taken out of the component.

Looks good. Just some concerns with how you are bringing in some of the already fetched data. I think that can be taken out of the component.
@ -1,51 +1,30 @@
const logger = require('winston');
module.exports = {
handleErrorResponse: function (originalUrl, ip, error, res) {
neb-b (Migrated from github.com) commented 2018-02-09 22:30:08 +01:00

Why changing to 200?

Why changing to 200?
neb-b (Migrated from github.com) commented 2018-02-09 22:53:34 +01:00

I don't have any experience using redux-saga so I might just not understand what is happening.

Generally you want XX_SUCCESS and XX_FAIL actions separated. That makes it a lot easier handling the data on the reducer.

I don't have any experience using `redux-saga` so I might just not understand what is happening. Generally you want `XX_SUCCESS` and `XX_FAIL` actions separated. That makes it a lot easier handling the data on the reducer.
neb-b (Migrated from github.com) commented 2018-02-09 22:55:57 +01:00

If these aren't being used anywhere else, I don't think they need to be separated.

IMO it would make these saga files easier to understand/follow, but not a big issue

If these aren't being used anywhere else, I don't think they need to be separated. IMO it would make these saga files easier to understand/follow, but not a big issue
bones7242 (Migrated from github.com) reviewed 2018-02-13 05:13:54 +01:00
@ -1,51 +1,30 @@
const logger = require('winston');
module.exports = {
handleErrorResponse: function (originalUrl, ip, error, res) {
bones7242 (Migrated from github.com) commented 2018-02-13 05:13:54 +01:00

I am trying to create a pattern where the server responds with a 200 as long as it processed the request correctly, and then responds with a success and a message or data field. I.e. {"success": "false", "message": "connection refused. The daemon may not be running"}. This way, if the client wanted some data but the daemon was off, it would get a 200 response that the request was completed, and then the client could check the response to see if it was successful or not and whether the data is present. My goal was to help myself (and api users) to distinguish between an error where something was unavailable but the request was handled properly (resulting in a 200) vs a request that I thought would be handled but wasn't, resulting in some other error. Thoughts?

I am trying to create a pattern where the server responds with a 200 as long as it processed the request correctly, and then responds with a `success` and a `message` or `data` field. I.e. `{"success": "false", "message": "connection refused. The daemon may not be running"}`. This way, if the client wanted some data but the daemon was off, it would get a `200` response that the request was completed, and then the client could check the response to see if it was successful or not and whether the data is present. My goal was to help myself (and api users) to distinguish between an error where something was unavailable but the request was handled properly (resulting in a `200`) vs a request that I thought would be handled but wasn't, resulting in some other error. Thoughts?
neb-b (Migrated from github.com) reviewed 2018-02-13 05:45:11 +01:00
@ -1,51 +1,30 @@
const logger = require('winston');
module.exports = {
handleErrorResponse: function (originalUrl, ip, error, res) {
neb-b (Migrated from github.com) commented 2018-02-13 05:45:11 +01:00

I think proper error codes would still be preferable. Even if the request is handled properly, there is still an error. If I see a 200 response, I assume my request was successful. The fetch api handles errors really nicely too, so I think if anything you are creating more work.

fetch('spee.ch/xxx')
  .then((data) => ...)         // successful
  .catch((error) => ...)       // not successful



fetch('spee.ch/xxx')
  .then((data) => {           // maybe successful?
    if (!data.success) {
      throw new Error()
   }
    ...                       // ok now I know it was successful
  })
  .catch((err) => ... )       // not successful

In my opinion it doesn't really matter if a request was handled successfully. If the data doesn't make it, it still fails. You can still add info with the error message and code.

I think proper error codes would still be preferable. Even if the request is handled properly, there is still an error. If I see a 200 response, I assume my request was successful. The `fetch` api handles errors really nicely too, so I think if anything you are creating more work. ``` fetch('spee.ch/xxx') .then((data) => ...) // successful .catch((error) => ...) // not successful fetch('spee.ch/xxx') .then((data) => { // maybe successful? if (!data.success) { throw new Error() } ... // ok now I know it was successful }) .catch((err) => ... ) // not successful ``` In my opinion it doesn't really matter if a request was handled successfully. If the data doesn't make it, it still fails. You can still add info with the error message and code.
neb-b (Migrated from github.com) requested changes 2018-02-13 06:16:02 +01:00
neb-b (Migrated from github.com) left a comment

I think you are nearly there with the mapStateToProps improvements. Just need to pull the last bit of un-needed logic out of the component.

I think you are nearly there with the `mapStateToProps` improvements. Just need to pull the last bit of un-needed logic out of the component.
neb-b (Migrated from github.com) commented 2018-02-13 06:05:56 +01:00

This probably shouldn't be called XXX_ASYNC since it isn't async

This probably shouldn't be called `XXX_ASYNC` since it isn't async
neb-b (Migrated from github.com) commented 2018-02-13 06:13:36 +01:00

I think you are still creating more work than necessary with this. In my opinion previousRequest shouldn't even exist. In the mapStateToProps you should be able to map the asset from your state into the component. If !asset then make the request.

I also think onShowNewAsset and onNewRequest can be combined. More specifically I don't think onShowNewAsset is needed. It might just be my lack of understanding with the current data flow, but you shouldn't need to manually say "show this asset". A better approach would be "select the asset with this id".

I think you are still creating more work than necessary with this. In my opinion `previousRequest` shouldn't even exist. In the `mapStateToProps` you should be able to map the `asset` from your state into the component. If `!asset` then make the request. I also think `onShowNewAsset` and `onNewRequest` can be combined. More specifically I don't think `onShowNewAsset` is needed. It might just be my lack of understanding with the current data flow, but you shouldn't need to manually say "show this asset". A better approach would be "select the asset with this id".
neb-b (Migrated from github.com) commented 2018-02-13 06:15:15 +01:00

Same comments above about previousRequest. I think a more understandable approach would just be:

if (!channel) this.props.onNewChannelRequest(...)
Same comments above about `previousRequest`. I think a more understandable approach would just be: ``` if (!channel) this.props.onNewChannelRequest(...) ```
bones7242 (Migrated from github.com) reviewed 2018-02-13 21:21:24 +01:00
@ -1,51 +1,30 @@
const logger = require('winston');
module.exports = {
handleErrorResponse: function (originalUrl, ip, error, res) {
bones7242 (Migrated from github.com) commented 2018-02-13 21:21:24 +01:00

That makes a lot of sense, and I was running into a lot of the latter situation where I had to check success in addition to checking for errors. I updated it and that cleaned up the response handling a lot.

That makes a lot of sense, and I was running into a lot of the latter situation where I had to check `success` in addition to checking for errors. I updated it and that cleaned up the response handling a lot.
bones7242 (Migrated from github.com) reviewed 2018-02-14 02:17:20 +01:00
bones7242 (Migrated from github.com) commented 2018-02-14 02:17:20 +01:00

Ok, I think I'm getting closer. I was able to do away with onShowNewAsset and combine the needed logic from its action (retrieving the asset's claim data) into onNewRequest. That allowed me to remove previousRequest from the props I am passing to the <ShowAsset /> component. However, I am still checking for a previousRequest in the mapStateToProps function. Do you see a way to avoid that step altogether? The reason for storing and checking the previous requests is to avoid having to retrieve new information for a request that was already made (i.e. to avoid having to request the full claimId from the server). I'm trying to figure out if that can be skipped or consolidated, but I am not sure how.

Ok, I think I'm getting closer. I was able to do away with `onShowNewAsset` and combine the needed logic from its action (retrieving the asset's claim data) into `onNewRequest`. That allowed me to remove `previousRequest` from the props I am passing to the `<ShowAsset />` component. However, I am still checking for a `previousRequest` in the mapStateToProps function. Do you see a way to avoid that step altogether? The reason for storing and checking the previous requests is to avoid having to retrieve new information for a request that was already made (i.e. to avoid having to request the full `claimId` from the server). I'm trying to figure out if that can be skipped or consolidated, but I am not sure how.
bones7242 (Migrated from github.com) reviewed 2018-02-14 02:18:20 +01:00
bones7242 (Migrated from github.com) commented 2018-02-14 02:18:19 +01:00

See above re: previous request. I mostly fixed this, but not sure if more consolidation can be done.

See above re: previous request. I mostly fixed this, but not sure if more consolidation can be done.
bones7242 commented 2018-02-14 21:32:12 +01:00 (Migrated from github.com)

@seanyesmunt I was able to pull the request validation out of <ShowAsset /> and remove that component entirely so that <ShowPage /> triggers the onNewChannelRequest or onNewAssetRequest actions. Those actions then retrieve the data and create the request along with the asset or channel. I also consolidated the channelRequests and assetRequests into one requestList. What do you think?

@seanyesmunt I was able to pull the request validation out of `<ShowAsset />` and remove that component entirely so that `<ShowPage />` triggers the `onNewChannelRequest` or `onNewAssetRequest` actions. Those actions then retrieve the data and create the `request` along with the `asset` or `channel`. I also consolidated the `channelRequests` and `assetRequests` into one `requestList`. What do you think?
neb-b commented 2018-02-14 22:30:50 +01:00 (Migrated from github.com)

@billbitt Looks great. The components are definitely a lot simpler. I'm just now thinking that most of the logic inside react/containers/ShowPage/view.jsx can be moved into redux still. You can fetch the actual asset directly after fetching the longForm if you haven't seen that longForm before. No need to wait for the ShowPage to render to make the actual asset request.

Not a super big deal, but I think that should be done at some point. I started on some pseudo code and got a little carried away lol. But I think it could look something like this

// ShowPage.jsx
class ShowPage extends React.Component {
  componentDidMount() {
    this.props.handleShowPageUri(this.props.match.params)
  }

  render() { ... }
}

// Asset redux saga
function handleShowPageUri({ uri }) {
  if (!isValidUri(uri)) { // parse the uri just to make sure its valid
    dispatch(someError())
    return
  }

  // If this uri is in the request list, it's already been fetched
  if (state.requestsList[uri]) {
    return;
  }

  // Haven't seen this uri before
  return getLongForm(uri)
    .then((longForm) => {
      dispatch(addUriToRequestsList(uri))

      if (state.assetList[longForm]) {
        // we already have it, do nothing
        return
      } else {
        fetchAsset(uri)
          .then((asset) => {
            dispatch(addAsset(asset))
          })
      }
    })
}
@billbitt Looks great. The components are definitely a lot simpler. I'm just now thinking that most of the logic inside `react/containers/ShowPage/view.jsx` can be moved into redux still. You can fetch the actual asset directly after fetching the longForm if you haven't seen that longForm before. No need to wait for the `ShowPage` to render to make the actual asset request. Not a super big deal, but I think that should be done at some point. I started on some pseudo code and got a little carried away lol. But I think it could look something like this ``` // ShowPage.jsx class ShowPage extends React.Component { componentDidMount() { this.props.handleShowPageUri(this.props.match.params) } render() { ... } } // Asset redux saga function handleShowPageUri({ uri }) { if (!isValidUri(uri)) { // parse the uri just to make sure its valid dispatch(someError()) return } // If this uri is in the request list, it's already been fetched if (state.requestsList[uri]) { return; } // Haven't seen this uri before return getLongForm(uri) .then((longForm) => { dispatch(addUriToRequestsList(uri)) if (state.assetList[longForm]) { // we already have it, do nothing return } else { fetchAsset(uri) .then((asset) => { dispatch(addAsset(asset)) }) } }) } ```
neb-b (Migrated from github.com) approved these changes 2018-02-14 22:31:07 +01:00
bones7242 commented 2018-02-15 03:12:37 +01:00 (Migrated from github.com)

@seanyesmunt I moved all that logic out of <ShowPage /> and into a handleShowPageUri action. That action parses it, validates it, and then triggers a onNewChannelRequest or onNewAssetRequest action. In those actions, I added checks to the store to get the lists like in your pseudocode. Can you check it out?

@seanyesmunt I moved all that logic out of `<ShowPage />` and into a `handleShowPageUri` action. That action parses it, validates it, and then triggers a `onNewChannelRequest` or `onNewAssetRequest` action. In those actions, I added checks to the `store` to get the lists like in your pseudocode. Can you check it out?
neb-b (Migrated from github.com) reviewed 2018-02-15 03:30:03 +01:00
@ -0,0 +9,4 @@
// claim will be an asset claim
// the identifier could be a channel or a claim id
let isChannel, channelName, channelClaimId, claimId, claimName, extension;
try {
neb-b (Migrated from github.com) commented 2018-02-15 03:30:03 +01:00

I think you need to add a state check here too. To see if that request exists. It looks like it will add the request to the list every time.

I think you need to add a state check here too. To see if that request exists. It looks like it will add the request to the list every time.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: LBRYCommunity/spee.ch#343
No description provided.