Skip to main content
Code Review

Return to Question

edited tags
Link
200_success
  • 145.5k
  • 22
  • 190
  • 478
Bumped by Community user
Tweeted twitter.com/StackCodeReview/status/987769077061111808
deleted 554 characters in body; edited title
Source Link

FlightList application as part of a job interview

I recently had an homework assignment as a part of a job interview, which I didn't pass. I wasn't sure I designed this tiny application correctly and I wanted to hear your opinions. I often find myself unsure of how to design React components (which should be smart, dumb, hold data, pass callbacks etc).

I will post the requirements, and my design and code, and then ask some questions regarding it at the end.

I would really appreciate any responses and answers : )

(sorry this post is a bit long)

The requirements wereare as follows:

Thank you for you time and answers : )

FlightList application as part of a job interview

I recently had an homework assignment as a part of a job interview, which I didn't pass. I wasn't sure I designed this tiny application correctly and I wanted to hear your opinions. I often find myself unsure of how to design React components (which should be smart, dumb, hold data, pass callbacks etc).

I will post the requirements, and my design and code, and then ask some questions regarding it at the end.

I would really appreciate any responses and answers : )

(sorry this post is a bit long)

The requirements were as follows:

Thank you for you time and answers : )

FlightList application

The requirements are as follows:

Source Link
Dvirski
  • 141
  • 1

FlightList application as part of a job interview

I recently had an homework assignment as a part of a job interview, which I didn't pass. I wasn't sure I designed this tiny application correctly and I wanted to hear your opinions. I often find myself unsure of how to design React components (which should be smart, dumb, hold data, pass callbacks etc).

I will post the requirements, and my design and code, and then ask some questions regarding it at the end.

I would really appreciate any responses and answers : )

(sorry this post is a bit long)

The requirements were as follows:

Create a Flight app service using React that does the following:

Login page:

  • When entering username: "user" and password: "password", the user will be routed to the app page.
  • otherwise - an error message should be presented.

Flight list page:

  • Table of flights with the fields: from,​ to​, departureTime​, landingTime​, price.​
  • If there are no flights, display "No flights listed" in the first table row.
  • Add the ability to filter the flights by flight destination

Add new flight page with a form:

  • On submit, the new flight will be added to the list.

My design:

I separated components into App, Login and FlightList.

App: Contained the routes, and the handleLogin callback passed to the Login Component.

Login: Contained inputs for user, pass. Each change in inputs updated the components state and when clicking the button I invoked the handleLogin cb and sent it the state.

FlightList: After component mounts I call a fake api returning a promise with array of flights, and set state with the array. then map over it to render a table. I also save another copy of the array in order to allow filtering by flight destination. The component has a text input, that when changed filters the copy of the array and setting it as the main array. This component also has a Modal that helps create new flight.

My code:

index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
 <BrowserRouter>
 <App />
 </BrowserRouter>
 , document.getElementById('root'));
registerServiceWorker();

App.js:

import React, { Component } from 'react';
import './App.css';
import Login from "./Components/Login/Login";
import FlightsList from "./Components/FlightsList/FlightsList";
import { Link, Route, Switch } from 'react-router-dom';
import { withRouter } from 'react-router-dom';
class App extends Component {
 handleLogin = (loginObject) => {
 if( loginObject.userName === 'user' && loginObject.password === 'pass') {
 this.props.history.push({
 pathname: '/flights',
 });
 }
 };
 render() {
 return (
 <Switch>
 <Route exact path="/" component={() => <Login doLogin={this.handleLogin} />}/>
 <Route path="/flights" component={FlightsList}/>
 </Switch>
 );
 }
}
export default withRouter(App);

Login.js

import React from 'react';
import PropTypes from 'prop-types';
import './Login.css';
import { withRouter } from 'react-router-dom';
class Login extends React.Component {
 static propTypes = {
 doLogin: PropTypes.func.isRequired,
 };
 handleChangedInput = (e) => {
 this.setState({
 [e.target.name]: e.target.value,
 });
 };
 sendLoginInfo = () => {
 this.props.doLogin(this.state);
 };
 render() {
 return(
 <div className="login-div">
 <h2>Login</h2>
 <div>
 <label>User Name: </label>
 <input name="userName" type="text" onChange={this.handleChangedInput}/>
 </div>
 <div>
 <label>Password: </label>
 <input name="password" type="password" onChange={this.handleChangedInput} />
 </div>
 <button onClick={this.sendLoginInfo} className="login-btn" name="button">{'Login'}</button>
 </div>
 );
 }
}
export default withRouter(Login);

FlightList.js:

import React, { Component } from 'react';
import './FlightsList.css';
import getFlights from '../../helpers/api';
import { Table, Button } from 'react-bootstrap';
import Modal from 'react-modal';
const customStyles = {
 content : {
 top : '50%',
 left : '50%',
 right : 'auto',
 bottom : 'auto',
 marginRight : '-50%',
 transform : 'translate(-50%, -50%)'
 }
};
export default class FlightsList extends Component {
 state = {
 flights: null,
 originalFlights: null,
 modalIsOpen: false,
 };
 componentDidMount() {
 getFlights()
 .then( flights => {
 this.setState({
 flights,
 originalFlights: flights,
 });
 });
 }
 openModal = () => {
 this.setState({modalIsOpen: true});
 };
 closeModal = () => {
 this.setState({modalIsOpen: false});
 };
 renderFlights() {
 return (
 this.state.flights.map( flight => (
 <tr key={flight.id}>
 <td>{flight.from}</td>
 <td>{flight.to}</td>
 <td>{flight.departureTime}</td>
 <td>{flight.landingTime}</td>
 <td>{flight.price}</td>
 </tr>
 ))
 );
 }
 rednerNoFlights() {
 return (
 <tr colSpan={5}>{'No Flights'}</tr>
 );
 }
 renderLoading() {
 return (
 <tr colSpan={5}><td>{'Loading'}</td></tr>
 );
 }
 handleKeyPress = (e) => {
 if(e.key == 'Enter'){
 if( e.target.value === '') {
 this.setState({
 flights: this.state.originalFlights,
 });
 }
 else {
 this.setState({
 flights: this.state.flights.filter(flight => flight.to === e.target.value),
 originalFlights: this.state.flights,
 });
 }
 }
 };
 handleDetailsSubmit = (e) => {
 const newFlight = {
 id: this.state.flights.length,
 from: this.refs.from.value,
 to: this.refs.to.value,
 departureTime: this.refs.departure.value,
 landingTime: this.refs.landing.value,
 price: this.refs.price.value,
 };
 this.setState({
 flights: [...this.state.flights, newFlight],
 originalFlights: [...this.state.flights, newFlight],
 modalIsOpen: false,
 });
 };
 renderModal = () => {
 return (
 <Modal
 isOpen={this.state.modalIsOpen}
 onRequestClose={this.closeModal}
 style={customStyles}
 contentLabel="Example Modal"
 >
 <div>
 <form className="form_style">
 <p>
 <label>{'from:'}</label>
 <input type="text" ref="from" name="from" />
 </p>
 <p>
 <label>{'to: '}</label>
 <input type="text" ref="to" name="to" />
 </p>
 <p>
 <label>{'Departure Time: '}</label>
 <input type="text" ref="departure" name="departure_time" />
 </p>
 <p>
 <label>{'Landing Time: '}</label>
 <input type="text" ref="landing" name="landing_time" />
 </p>
 <p>
 <label>{'Price: '}</label>
 <input min="0" ref="price" type="number" name="price" />
 </p>
 <div className="button_div">
 <Button onClick={this.handleDetailsSubmit} bsStyle="primary" bsSize="small">
 {'Save'}
 </Button>
 </div>
 </form>
 </div>
 </Modal>
 );
 };
 render() {
 return (
 <div>
 <h1>Flights</h1>
 <div>
 <label>{'Filter: '}</label>
 <input type="text" onKeyPress={this.handleKeyPress}/>
 </div>
 <div>
 <button onClick={this.openModal} >{'Add Flight'}</button>
 </div>
 {this.renderModal()}
 <Table striped bordered condensed hover>
 <thead>
 <tr>
 <th>from</th>
 <th>to</th>
 <th>Departure Time</th>
 <th>Landing Time</th>
 <th>Price</th>
 </tr>
 </thead>
 <tbody>
 {!this.state.flights ? this.renderLoading() : ( this.state.flights.length > 0 ? this.renderFlights() : this.rednerNoFlights() )}
 </tbody>
 </Table>
 </div>
 );
 }
}

api.js:

export default function getFlights() {
 return new Promise((resolve, reject) => {
 setTimeout(() => resolve([
 {
 id: 0,
 from: 'Tel-Aviv',
 to: 'New-York',
 departureTime: 55,
 landingTime: 55,
 price: 300,
 },
 {
 id: 1,
 from: 'Tel-Aviv',
 to: 'Amsterdam',
 departureTime: 55,
 landingTime: 55,
 price: 300,
 },
 {
 id: 2,
 from: 'Tel-Aviv',
 to: 'New-York',
 departureTime: 55,
 landingTime: 55,
 price: 300,
 }
 ]), 2000);
 });
}

Some questions:

  • What would you do differently?
  • Is it a good practice to pass callback to the login component, or should the login component hold the handleLogin function itself?
  • Is it a good practice to store user and pass in the Login components state and send it as I did?
  • Would you create a add flight component containing the Modal and send it the handleDetailsSubmit function, instead of rendering it inside the flightList component?
  • Would you handle the no-flights and loading display differently?
  • How would you handle the filtering differently? I know it's not very efficient.
  • Would you handle routing differently?
  • I know I should have added some validations, but I didn't have time.

Thank you for you time and answers : )

default

AltStyle によって変換されたページ (->オリジナル) /