React

Date: 2019-08-16

https://devhints.io/react

https://codeburst.io/getting-started-with-react-router-5c978f70df91

https://reactcheatsheet.com/

React components

https://reakit.io/

npx create-react-app <app> --typescript
npm start

React-Bootstrap

https://react-bootstrap.github.io/getting-started/why-react-bootstrap/

# install yarn
npm i -g yarn
yarn add @blueprintjs/core @blueprintjs/icons @blueprintjs/datetime @blueprintjs/select @blueprintjs/timezone

Things to learn:

  • PureComponent => class MessageBox extends PureComponent
  • ForceUpdate (Component) => this.forceUpdate() // iso this.setState({ … })
  • Properties to child: <Child {…this.props} />
componentDidUpdate (prevProps, prevState, snapshot) 	// Use setState() here, but remember to compare props
shouldComponentUpdate (newProps, newState) 	// Skips render() if returns false
render() 	// Render
componentDidUpdate (prevProps, prevState) 	// Operate on the DOM here
//Dom reference
class MyComponent extends Component {
  render () {
    return <div>
      <input ref={el => this.input = el} />
    </div>
  }
  componentDidMount () {
    this.input.focus()
  }
}
// Lists
class TodoList extends Component {
  render () {
    const { items } = this.props

    return <ul>
      {items.map(item =>
        <TodoItem item={item} key={item.key} />)}
    </ul>
  }
}


<div
    onMouseEnter={() => setText("You hovered over me!")}
    onMouseLeave={() => setText("Hover over me to see changes")}
>
function HelloWorld (props){
    const [text, setText] = useState(props.text)
    
    return (
        <div>{text}</div>
    )
}


render() {
const [isLoading, setIsLoading] = useState(false);
return (
  <div className="App">
    {isLoading ? <div>Loading...</div> : <div>Done!</div>}
  </div>
);
}


interface SquareProps {
  value: string
  onClick: (event: any) => void;
}

export function Square(props: SquareProps) {
  return (
    <button className="square" onClick={props.onClick}>
      {props.value}
    </button>
  );
}
import React from "react";
import { Square } from "./Square";

interface BoardProps {
  squares: string[];
  onClick: (event: any) => void;
}

interface BoardState {
  squares: string[];
  xIsNext: boolean;
}
export class Board extends React.Component<BoardProps, BoardState> {
  renderSquare(i: number) {
    return <Square  value={this.props.squares[i]} onClick={() => this.props.onClick(i)} />;
  }
  render() {
    return (
      <div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}
import React from "react";
import { Board } from "./Board";

interface GameState {
  history: any[];
  xIsNext: boolean;
  stepNumber: number;
}
export class Game extends React.Component<{}, GameState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      history: [{
        squares: Array(9).fill(null),
      }],
      stepNumber: 0,
      xIsNext: true
    };
  }
  handleClick(i: number) {
    const history = this.state.history.slice(0, this.state.stepNumber + 1);
    const current = history[history.length - 1];
    const squares = current.squares.slice();
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      history: history.concat([{
        squares: squares,
      }]),
      stepNumber: history.length,
      xIsNext: !this.state.xIsNext
    });
  }
  jumpTo(step: number) {
    this.setState({
      stepNumber: step,
      xIsNext: (step % 2) === 0,
    });
  }
  render() {
    const history = this.state.history;
    const current = history[this.state.stepNumber];
    const winner = calculateWinner(current.squares);

    const moves = history.map((step, move) => {
      const desc = move ?
        'Go to move #' + move :
        'Go to game start';
      return (
        <li key={move}>
          <button onClick={() => this.jumpTo(move)}>{desc}</button>
        </li>
      );
    });

    let status;
    if (winner) {
      status = 'Winner: ' + winner;
    } else {
      status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
    }
    return (
      <div className="game">
        <div className="game-board">
          <Board
            squares={current.squares}
            onClick={(i) => this.handleClick(i)}
          />
        </div>
        <div className="game-info">
          <div>{status}</div>
          <ol>{moves}</ol>
        </div>
      </div>
    );
  }
}

function calculateWinner(squares: string[]) {
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}
import React from "react";

interface LoginProps {}
interface LoginState {
    username: string;
    password: string;
}
export class Login extends React.Component<LoginProps, LoginState> {
    constructor(props: LoginProps) {
      super(props);
      this.state = {
        username: "",
        password: ""
      };  
      this.handleChange = this.handleChange.bind(this);
      this.handleSubmit = this.handleSubmit.bind(this);
    }
    handleChange(event: React.ChangeEvent<HTMLInputElement>) {
        const target = event.target;
        (this.state as any)[target.name] = target.value;
        this.setState(this.state);
    }
    handleSubmit(event: React.FormEvent<HTMLFormElement>) {
        console.log('Form was submitted: ', this.state);
        event.preventDefault();
    }
    render() {
      return (
        <form onSubmit={this.handleSubmit}>
          <label>
            User name:
            <input
              name="username"
              type="text"
              value={this.state.username}
              onChange={this.handleChange} />
          </label>
          <br />
          <label>
            Password:
            <input
              name="password"
              type="password"
              value={this.state.password}
              onChange={this.handleChange} />
          </label>
          <button type="submit">Login</button>
        </form>
      );
    }
  }
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { Router, Route, RouteComponentProps } from 'react-router';
import { Link, HashRouter } from 'react-router-dom';

function App() {
  return (
    <HashRouter> 
      <div>
        <Header />
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/topics" component={Topics} />
      </div>
    </HashRouter>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Topic({ match }: RouteComponentProps<{ id: string }>) {
  return <h3>Requested Param: {match.params.id}</h3>;
}

function Topics({ match }: RouteComponentProps<{}>) {
  return (
    <div>
      <h2>Topics</h2>

      <ul>
        <li>
          <Link to={`${match.url}/components`}>Components</Link>
        </li>
        <li>
          <Link to={`${match.url}/props-v-state`}>Props v. State</Link>
        </li>
      </ul>

      <Route path={`${match.path}/:id`} component={Topic} />
      <Route
        exact
        path={match.path}
        render={() => <h3>Please select a topic.</h3>}
      />
    </div>
  );
}

function Header() {
  return (
    <ul>
      <li>
        <Link to="/">Home</Link>
      </li>
      <li>
        <Link to="/about">About</Link>
      li>
      <li>
        <Link to="/topics">Topics</Link>
      </li>
    </ul>
  );
}
export default App;
import React from "react";

interface TextInputProps {
  label: string;
  name: string;
  value?: string;
  isPassword?: boolean;
  onChange: (value: string) => void
}
interface TextInputState {
    value: string;
    type: string;
}
export class TextInput extends React.Component<TextInputProps, TextInputState> {
    constructor(props: TextInputProps) {
      super(props);
      this.state = {
        value: this.props.value || "",
        type: this.props.isPassword ? "password" : "text"
      };  
      this.handleChange = this.handleChange.bind(this);
    }
    handleChange(event: React.ChangeEvent<HTMLInputElement>) {
        const value = event.target.value;
        if (value !== this.state.value) {
          this.setState({ value });
          this.props.onChange(value);
        }
    }
    render() {
      return (
          <label>
            {this.props.label}
            <input
              name={this.props.name}
              type={this.state.type}
              value={this.state.value}
              onChange={this.handleChange} />
          </label>
      );
    }
  }
const Wrapper = ({children}) => (
  <div>
    <div>header</div>
    <div>{children}</div>
    <div>footer</div>
  </div>
);

const App = () => <div>Hello</div>;

const WrappedApp = () => (
  <Wrapper>
    <App/>
  </Wrapper>
);
import React from 'react';
import './App.css';
import { Wiring } from './wiring/Wiring';
import { Route, Switch, Redirect } from 'react-router';
import { HashRouter } from 'react-router-dom';
import { LoginView } from './view/LoginView';
import { MonthView } from './view/MonthView';
import { DayView } from './view/DayView';
import { NavMenu } from './components/NavMenu';
import { RedirectToToday, RedirectToLogin } from './view/Redirects';
import { appDomain } from './domain/AppDomain';

new Wiring();

function LoginRoutes() {
  return (
    <HashRouter> 
      <Switch>
        <Route exact path="/login" component={LoginView} />
        <Route path="*" component={RedirectToLogin} />
      </Switch>
    </HashRouter>
    );
}

function PrivateRoutes() {
  return (
    <HashRouter> 
      <NavMenu />
      <Switch>
        <Route exact path="/" component={RedirectToToday} />
        <Route path="/day" component={DayView} />
        <Route path="/month" component={MonthView} />   
        <Route component={RedirectToToday} />
      </Switch>
    </HashRouter>
  );
}

export class App extends React.Component {
  render() {
    //const hasPermission = appDomain.IRequestPermission.hasPermission(null);
    const hasPermission = false;
    if (hasPermission) {
      return (<PrivateRoutes />)
    }
    return (<LoginRoutes />)
  }
}
export default App;
import React from "react";
import { dateAdd } from "../helpers/tsHelpers";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";

const day = 1000 * 60 * 60 * 24;

interface DateNavigatorProps {
    value?: Date | null;
    onChange: (value: Date | null) => void
}
interface DateNavigatorState {
    value?: Date | null;
}
export class DateNavigator extends React.Component<DateNavigatorProps, DateNavigatorState> {
    constructor(props: DateNavigatorProps) {
      super(props);
      this.state = {
        value: this.props.value
      };  
    }
    prevDate() {
        this.handleChange(dateAdd(this.state.value, -day));
    }
    nextDate() {
        this.handleChange(dateAdd(this.state.value, day));
    }
    handleChange(value:  Date | null) {
        if (value !== this.state.value) {
          this.setState({ value });
          this.props.onChange(value);
        }
    }
    static getDerivedStateFromProps(nextProps: DateNavigatorProps, prevState: DateNavigatorState): Partial<DateNavigatorState> | null {
        if (prevState.value !== nextProps.value) {
            return { value: nextProps.value };
        }
        return null;
    }
    render() {
      return (
          <div className="date-navigator">
            <div className="button-prev" onClick={this.prevDate.bind(this)}><</div>
            <div className="date-picker">
            <DatePicker
                todayButton={"Vandaag"}
                locale="nl-NL"
                selected={this.state.value}
                onChange={this.handleChange.bind(this)}
                dateFormat="iiii d MMMM yyyy"
            />
            </div>
            <div className="button-next" onClick={this.nextDate.bind(this)}>></div>
          </div>
      );
    }
  }
import React from 'react';
import { appDomain } from './domain/AppDomain';
import { PrivateRoutes } from './routes/PrivateRoutes';
import { PublicRoutes } from './routes/PublicRoutes';
import { RouteComponentProps, withRouter } from 'react-router';
import { INavigateTo } from './domain/ports/secondary/INavigateTo';
import { NavigationPlug } from './adapters/NavigationAdapter/NavigationPlug';
import { wiring } from './App';

export class RoutingWrapperBase extends React.Component<RouteComponentProps> {
  unlisten: Function | null = null;
  componentDidMount() {
    this.handleLocationChange(this.props.history.location);
    this.unlisten = this.props.history.listen(this.handleLocationChange);
  }
  componentWillUnmount() {
    if (this.unlisten) {
      this.unlisten();
    }
  }
  handleLocationChange(location: any) {
    // your staff here
    console.log(`location`, location);
  }
  render() {
    const navigateFn = (path: string, state?: any) => this.props.history.push(path, state);
    wiring.portResolver.registerInstance<INavigateTo>("INavigateTo", () => new NavigationPlug(navigateFn));
    const hasPermission = appDomain.IRequestPermission.hasPermission(null);
    if (hasPermission) {
      return (<PrivateRoutes />);
    }
    return (<PublicRoutes />);
  }
}

export const RoutingWrapper = withRouter(RoutingWrapperBase);
import React from "react";
import { IEventSubscription } from "../../domain/ports/primary/IEventHandler";
import { appDomain } from "../../domain/AppDomain";

export class BaseComponent<P = {}, S = {}, SS = any> extends React.Component<P, S, SS> {
    private _unmounted = false;
    private _subscriptions: IEventSubscription[];
    constructor(props: P) {
      super(props);
      this._subscriptions = [];
    }
    mounted() {
      return !this._unmounted;
    }
    componentDidMount() {
      // add mount code
    }
    componentWillUnmount() {
      this._unmounted = true;
      this._subscriptions.forEach(s => s.unsubscribe());
    }
    addEventListener(element: EventTarget, event: string, handler: EventListenerOrEventListenerObject) {
        element.addEventListener(event,  handler);
        this._subscriptions.push({ unsubscribe: () => element.removeEventListener(event, handler) });
    }
    subscribe(event: string, fn: (data: any) => void) {
      return appDomain.IEventHandler.subscribe(event, fn);
    }
    subscribeInterval(fn: Function, ms: number): void {
        const interval = setInterval(fn, ms);
        this._subscriptions.push({ unsubscribe: () => clearInterval(interval) });
    }
    translate(key: string, params?: any): string {
      return appDomain.IRequestTranslation.translate(key, params);
    }
    navigateTo(url: string, params: any = {}): void {
      appDomain.INavigator.navigateTo(url, params);
    }
  }
24910cookie-checkReact