- End of 180 period input date
- Trip Inputs
- Add another trip button
- Remove trip button
- Calculate Button
- Result output
- End of 180 days date
- Start of 180 days date
- Array of trips
- Trip start date
- Trip end date
- Calculation
- Overlapping trips over start or end of period have the correct dates (they adopt the start or end period date)
- Filter trips that are outside the travel period
- Total up the days for valid trips
- Show users result
- Have one add trip button?
- Align elements better
- Tests
- Business Logic (jest?)
- UI (React Testing Library)
- Get CI up and running (github actions)
- Codecov
- Deploy (Netlify? AWS? Github Pages?)
- lodash for array management (deleting arrays, updating specific elements of an array in an immutable way)
- moment/date-fns for datetime handling
- Elm - purely functional language to build reactive JS apps, similar to Redux
- Redux
- Jest
- React testing library
- React Bootstrap
- Codecov
- LogRocket (redux logging)
- Flow (linter)
- Snapshot testing with jest
- Railway for cloud storage
Here's how I might approach an app where you initially have one row with two date inputs, an "add new row" button, and when you change the date inputs the calculation changes automatically:
- The parent component handles all state
- We keep track of a hash and a list for trips. The hash holds the data about the trips, the list specifies the ordering of the trips
- The hash looks like e.g.
{ "trip1": {start_date: A, end_date: B}, "trip2": {start_date: C, end_date: D}}
- i.e. it's a hash where the keys are the trip id and the values are a hash of start and end dates
- The list looks like e.g.
["trip1", "trip2"]
- i.e. it's the trip ids in a specific order
- We have to generate trip ids to keep track of which trip we're changing (can use random strings)
That'd look something like:
class MyComponent extends React.Component {
state = {
trips: {"initial": {start_date: undefined, end_date: undefined} }, // holds data for each trip
trip_ids: ["initial"], // determines ordering of trips
} handleStartDateChange = (trip_id, date) => {
// update the start date of the trip corresponding to trip_id
} handleEndDateChange = (trip_id, date) => {
// update the end date of the trip corresponding to trip_id
} addNewRow = () => {
// push a new item to this.state.trip_ids with a random id
// add a blank trip to this.state.trips with that trip_id just generated
} calculation = () => {
return 123 // some calculation involving this.state.trips
} render() {
return (
<div>
{this.state.trip_ids.map((trip_id) => (
<Trip
startDate={this.state.trips[trip_id].startDate}
endDate={this.state.trips[trip_id].endDate}
onStartDateChange={(date) => this.handleStartDateChange(trip_id, date)}
onEndDateChange={(date) => this.handleEndDateChange(trip_id, date)}
/>
))}
<AddNewRowButton onClick={this.addNewRow} />
{this.calculation()}
</div>
)
}
}
It's actually a bit trickier than I thought
also Trip
now takes startDate
and endDate
making a it a fully controlled component
A JavaScript library for building user interfaces (11 kB)
https://reactjs.org/docs/forms.html#controlled-components
The trickiness comes because we want to keep track of the order of trips - a common trick to do that is to split up the data into the attributes and the ordering (we do this in the Banking app)
a hash is easy to update, but it has no inherent ordering. an array has ordering but is tricky to update
This project was bootstrapped with Create React App.
In the project directory, you can run:
Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.
The page will reload if you make edits.
You will also see any lint errors in the console.
Launches the test runner in the interactive watch mode.
See the section about running tests for more information.
Builds the app for production to the build
folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Your app is ready to be deployed!
See the section about deployment for more information.
Note: this is a one-way operation. Once you eject
, you can’t go back!
If you aren’t satisfied with the build tool and configuration choices, you can eject
at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject
will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
You don’t have to ever use eject
. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
You can learn more in the Create React App documentation.
To learn React, check out the React documentation.
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify