leftiness / react-demo

Other
0 stars 0 forks source link

Set up reservations UI #19

Open leftiness opened 8 years ago

leftiness commented 8 years ago
leftiness commented 8 years ago

Ugh. I just spent like an hour an a half making a bunch of things, writing a bunch of tests... Wasted. I was putting state on redux instead of putting it in the URL. The state has to be in the URL because I want deep links to work obviously... What was I thinking... -.-

I need to be able to keep track of what day the Reservations component is looking at. He also needs to be able to click a button and go back one day or click and go forward one day. In the future, he'll need a date picker that will let the user choose a day faster, but that's for later.

The Reservations component will be using a URL option called date. It'll be a unix timestamp. If I'm at site.com/#/reservations?date=12345, that means that they're looking at the day corresponding to that 12345 unix timestamp. When I read it and set it, I'll update it to the start of the day (moment.unix(timestamp).startOf('day').unix()).

Anyway. To make this happen, the yesterday button is actually a link (<a href={ href } className="button">). I need to create a new container of Link like I did with OptionToggleLink. This one will read the date URL option and set the Link component's href with the date option set to yesterday's timestamp.

leftiness commented 8 years ago

Maybe the reservations component should get the date prop so that it'll be able to update and to say whether componentShouldUpdate() later on.

The container will create fetchData() and storeData() functions. Something like this:

// Using this ownProps.service bit so I can test the function later by passing in a stub. Good pattern?

const mapStateToProps = (state, ownProps) => {
  const service = ownProps.service || MyImportedService;
  const fetchData = () => service.fetchData(get(state, 'navigation.location.options.date'));
  return { fetchData, otherThings };
};

const mapDispatchToProps = (dispatch. ownProps) => {
  const service = ownProps.service || MyImportedService;
  const storeData = (data) => dispatch(storeReservationAction(data));
  return { storeData , otherThings };

That fetchData function prop will return a promise. The component will use it like this:

componentDidMount() {
  this.props.fetchData().then((data) => this.props.storeData(data));
}

componentDidMount() happens right after render(). It'll flash with no reservations, storeData() will dispatch, the component will update and render() again with the fetched reservations. Is that a problem? Will it actually flash, or will that happen super fast behind the scenes? Not sure.

There's also the matter of how it'll handle the next rendering after that. If someone clicks the button to go to tomorrow's reservations, the application component will dispatch that onhashchange(), but it won't actually change the component which is being rendered because it'll be the same reservations component that should be rendered. It won't be a re-render. It'll be an update. So... maybe it'll send another fetchData() in componentWillReceiveProps() if the date prop changed. if the date prop didn't change (and etc other checks like maybe always fetch new data if the data is 30 seconds old or they click the button to check for new data), maybe it'll say no to componentShouldUpdate().

Maybe I could use the mergeProps() function on connect to merge this.props.fetchData().then((data) => this.props.storeData(data));. Something like this:

const mergeProps = (stateProps, dispatchProps, ownProps) => {
  const fetchData = stateProps.fetchData().then((data) => dispatchProps.storeData(data));
  delete stateProps.fetchData;
  delete dispatchProps.storeData;
  return { ...stateProps, ...dispatchProps, fetchData }
};

Guess I'll just test this stuff out...

leftiness commented 8 years ago

Reducing the scope of this. Adding the next/previous buttons #53 #62 and keeping track of the date #59 are part of other issues.