Enrutamiento

Instalar:

npm install react-router-dom

https://reacttraining.com/react-router/web/example/basic
https://learnwithparam.com/blog/basic-routing-in-react-router/
https://medium.com/@simonhoyos/enrutando-en-react-cd9e4ad6e3d3

Ejemplo:

import React from "react";
import {
BrowserRouter as Router,
        Switch,
        Route,
        Link,
        useParams,
        Redirect
        } from "react-router-dom";

export default function App() {
    return (
            <Router>
                <div>
                    <nav>
                        <ul>
                            <li>
                                <Link to="/">Home</Link>
                            </li>
                            <li>
                                <Link to="/about">About</Link>
                            </li>
                               <li>
                                <Link to="/acerca">Acerca</Link>
                            </li>
                            <li>
                                <Link to="/users">Users</Link>
                            </li>
                            <li>
                                <Link to="/usuario/1">us1</Link>
                            </li>
                            <li>
                                <Link to="/usuario/2">us2</Link>
                            </li>
                        </ul>
                    </nav>
            
            
                    <Switch>
                    <Route path="/usuario/:id"  component={Usuario} />
                       
                    <Route exact path="/about">
                        <About />
                    </Route>
                    <Route exact path="/acerca">
                        <Acerca />
                    </Route>
                    <Route path="/users" component={Users}/>
                    <Route exact path="/">
                        <Home />
                    </Route>
            
                    </Switch>
                </div>
            </Router>
            );
}

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

function About() {
    return <h2>About</h2>;
}
function Acerca() {
    return <Redirect to='/about' />;
}
function Users() {
    return <h2>Users</h2>;
}

function Usuario() {
    
    let {id} = useParams();

    return (
            <div>
                <h3>ID: {id}</h3>
            </div>
            );
}

JSX React

Fragmentos:

// Example 1: Using empty tags <></> 
  render() {
    return ( 
      <>
        <ComponentA />
        <ComponentB />
        <ComponentC />
      </>
    );
  }

// Example 2: Using React.Fragment 
  render() {
    return ( 
      <React.Fragment>
        <h1>An h1 heading</h1> 
        Some text here. 
        <h2>An h2 heading</h2> 
        More text here.
        Even more text here.
       </React.Fragment>
    ); 
  }

  // Example 3: Importing the Fragment
  import React, { Fragment } from 'react';

  render() {
    return ( 
      <Fragment>
        <h1>An h1 heading</h1> 
        Some text here. 
        <h2>An h2 heading</h2> 
        More text here.
        Even more text here.
      </Fragment>
    ); 
  }

  return [
      <li key="1">First item</li>, 
      <li key="2">Second item</li>, 
      <li key="3">Third item</li>
    ]; 
  }

Espacios:

 <div> 
    <span>My</span> 
    {' '}
    name is
    {' '} 
    <span>Carlos</span> 
  </div>

Spread atributes:

  const attrs = { 
    id: 'myId',
    className: 'myClass'
  };
   
  return <div {...attrs} />;

Condicionales:

<div> 
    {isLoggedIn && <LoginButton />} 
  </div>

  <div> 
    {isLoggedIn ? <LogoutButton /> : <LoginButton />} 
  </div>

Propiedades por defecto:

class Button extends React.Component { 
    render() { 
      return <button>{this.props.text}</button>;
    } 
  } 
   
  Button.propTypes = { 
    text: React.PropTypes.string
  };
   
  Button.defaultProps = { 
    text: 'Click me!'
  };

Tres maneras de hacer el binding de las funciones:

 class Button extends React.Component { 
    constructor(props) { 
      super(props);
   
      this.handleClick = this.handleClick.bind(this);
    } 
   
    handleClick() { 
      console.log(this);
    } 
   
    render() { 
      return <button onClick={this.handleClick} />;
    } 
  }

  class Button extends React.Component { 
    handleClick() { 
      console.log(this);
    } 
   
    render() { 
      return <button onClick={() => this.handleClick()} />;
    } 
  }

class Button extends React.Component {    
    handleClick = () => { 
      console.log(this);
    } 
   
    render() { 
      return <button onClick={this.handleClick} />;
    } 
  }

Cambiar estado:

// Incorrecto
this.setState({
  counter: this.state.counter + this.props.increment,
});

// Correcto
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

Ejercicio (I)

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class Boton extends Component {
    mas = () => {
        this.props.mas(0);
    }
    render() {

        return (
            <button onClick={this.mas}>+</button>
        );
    }
}
;
class Fila extends Component {

    menos = () => {
        this.props.menos(this.props.indice);
    }
    render() {
        return (
            <p><input type="number" value={this.props.valor}></input>
                <button onClick={this.menos}>-</button>
            </p>
        );
    }
}
class Total extends Component {
    render() {
        return (
            <p>Total:{this.props.suma}</p>
        )
    }
}
class Tabla extends Component {
    constructor(props) {
        super(props);
        this.state = {
            numeros: [1, 2, 90, 0, 45]
        };

    }
    mas = (valor) => {
        this.setState({ numeros: [...this.state.numeros, valor] });
    }
    menos = (index) => {
        this.setState({
            numeros: this.state.numeros.filter((numero, i) => i != index)
        });
    }
    render() {
        const filas = this.state.numeros.map((valor, index) => {
            return (<Fila key={index} indice={index} valor={valor} menos={this.menos} />);
        });
        const suma = this.state.numeros.reduce((a, b) => a + b, 0);
        return (
            <div>
                <Boton mas={this.mas} />
                {filas}
                <Total suma={suma} />
            </div>
        );
    }
}
;
class App extends Component {
    render() {
        return (
            <Tabla />
        );
    }
}



;
export default App;

Etiqueta color

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class Cuadrado extends Component {
    render() {
        const estilo = {
            padding: 10,
            margin: 20,
            display: "inline-block",
            backgroundColor: this.props.bgColor,
            width: 100,
            height: 100
        };
        return (
                <div style={estilo}>
                </div>
                );
    }
}
class Color extends Component {
    constructor(props) {
        super(props);
        this.state = {
            color: this.props.color
        };
    }
    cambio = (event) => {
        const {name, value} = event.target;
        this.setState({
            [name]: value
        })
        this.props.cambiaColor(value);
    }
    render() {
        return (
                <div>
                    <input type="text" name="color" value={this.state.color} onChange={this.cambio}/>
                </div>
                );
    }
}
class Tarjeta extends Component {
    constructor(props) {
        super(props);
        this.state = {
            color: "red"
        };
    }
    isColor = (strColor) => {
        const s = new Option().style;
        s.color = strColor;
        return s.color !== '';
    }

    shouldComponentUpdate(nextProps, nextState) {
        return this.isColor(nextState.color);

    }

    cambiaColor = (color) => {
         this.setState({color: color});
    }
    render() {
        return (
                <div>
                    <Cuadrado bgColor={this.state.color}/>
                    <Color color={this.state.color} cambiaColor={this.cambiaColor}/>
                
                </div>
                );
    }
}
;
class App extends Component
{
    render() {
        return (
                <Tarjeta/>
                );
    }
}

export default App;

Ciclo de vida de los componentes

Aquí están muy bien explicados:

https://www.gistia.com/understand-react-lifecycle-methods/

Código:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg';
import './App.css';
class Contador extends React.Component {
    render() {
        var textStyle = {
            fontSize: 72,
            fontFamily: "sans-serif",
            color: "#333",
            fontWeight: "bold"
        };
        console.log("render: Contador componente");

        return (
                <div style={textStyle}>
                    {this.props.display}
                </div>
                );
    }
}
class ContadorParent extends React.Component {
    constructor(props) {
        super(props);
        console.log("constructor: Valores por defecto");
        this.state = {
            count: 0
        };
        this.increase = this.increase.bind(this);
    
    }
    increase() {
        this.setState({
            count: this.state.count + 1
        });
    }

    static getDerivedStateFromProps(props, state) {
        console.log("getDerivedStateFromProps: Poco uso")
        return null;
    }
    componentDidUpdate(currentProps, currentState) {
        console.log("componentDidUpdate: El componente se ha actualizado");
    }
    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log("getSnapshotBeforeUpdate: El componente va a enviar sus cambios")
        return null;
    }
    componentDidMount() {
        console.log("componentDidMount: Componente insertado en el árbol DOM");
    }
    componentWillUnmount() {
        console.log("componentWillUnmount: Component a punto de ser eliminado del DOM");
    }
    shouldComponentUpdate(newProps, newState) {
        console.log("shouldComponentUpdate: ¿Hay que actualizar?");
        if (newState.count < 5) {
            console.log("shouldComponentUpdate: Sí, hay que actualizar");
            return true;
        } else {
            ReactDOM.unmountComponentAtNode(document.getElementById('root'));
            console.log("shouldComponentUpdate: No hay que actualizar");
            return false;
        }
    }
    render() {
        var backgroundStyle = {
            padding: 50,
            border: "#333 2px dotted",
            width: 250,
            height: 100,
            borderRadius: 10,
            textAlign: "center"
        };
        console.log("render: ContadorParent componente");
        return (
                <div style={backgroundStyle}>
                    <Contador display={this.state.count} />
                    <button onClick={this.increase}>
                        +
                    </button>
                     
                </div>
                );
    }
}
;
class App extends Component


{
    render() {
     
        return (
               <ContadorParent/>
                );
    }
}



;
export default App;

Formulario

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class Form extends Component {
    constructor(props) {
        super(props)

        this.initialState = {
            nombre: '',
            nota: '',
        }

        this.state = this.initialState;
    }
    cambio = event => {
        const {name, value} = event.target;

        console.log(name + "|" + value);
        this.setState({
            [name]: value
        })
    }
    enviar = () => {
        this.props.mas(this.state.nombre, this.state.nota);
        this.setState(this.initialState);
    }
    render() {
        return (
                <form>
                    <label>Nombre</label>
                    <input type="text" name="nombre" value={this.state.nombre} onChange={this.cambio} />
                    <label>Nota</label>
                    <input type="text" name="nota" value={this.state.nota} onChange={this.cambio} />
                    <input type="button" onClick={this.enviar} value="Añadir"/>
                </form>
                );
    }
}
class Cabecera extends Component {
    render() {

        return (
                <tr><th>Alumno</th><th>Nota</th></tr>
                );
    }
}
;
class Fila extends Component {
   
    eliminar=(e)=>{
        e.stopPropagation();
        this.props.menos(this.props.indice);
    }
    render() {
       
        return (
                <tr onClick={() => this.props.mas(this.props.nombre, this.props.nota)}>
                    <td>{this.props.nombre}</td><td>{this.props.nota}</td>
                            <td><input type="button" value="Borrar" onClick={this.eliminar}/></td>
                </tr>
                );
    }
}
;
class Tabla extends Component {
    constructor(props) {
        super(props);
        this.state = {
            alumnos: this.props.alumnos,
            cont: 0
        };
        this.mas = this.mas.bind(this);
    }
    mas(nombre, nota) {

        this.setState({alumnos: [...this.state.alumnos, {nombre: nombre, nota: nota}]});
    }
    menos = (index) => {
        this.setState({
            alumnos: this.state.alumnos.filter((alumno, i) => i != index)
        });
    }

    render() {

        const filas = this.state.alumnos.map((fila, index) => {
            return (<Fila key={index} indice={index} nombre={fila.nombre} nota={fila.nota} menos={this.menos} mas={this.mas}/>);
        }
        );
        return (
                <div>
                    <Form mas={this.mas}/>
                    <button onClick={() => this.mas("Ana", 6)}>Añadir</button> <button onClick={() => this.menos(0)}>Quitar</button>
                    <table>
                        <thead>
                        <Cabecera/></thead>
                        <tbody>
                            {filas}
                        </tbody>
                    </table>
                </div>
                );
    }
}
;
class App extends Component


{
    render() {
        const alumnos = [{nombre: "Ana", nota: 6}, {nombre: "Pep", nota: 4}, {nombre: "Eva", nota: 8}, {nombre: "Ot", nota: 7}];
        return (
                <Tabla alumnos={alumnos}/>
                );
    }
}



;
export default App;

Quitar elementos

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class Cabecera extends Component {
    render() {

        return (
                <tr><th>Alumno</th><th>Nota</th></tr>
                );
    }
}
;
class Fila extends Component {
    render() {
        return (
                <tr onClick={() => this.props.mas(this.props.nombre, this.props.nota)}>
                    <td>{this.props.nombre}</td><td>{this.props.nota}</td>
                </tr>
                );
    }
}
;
class Tabla extends Component {
    constructor(props) {
        super(props);
        this.state = {
            alumnos: this.props.alumnos,
            cont: 0
        };
        this.mas = this.mas.bind(this);
    }
    mas(nombre, nota) {

        this.setState({alumnos: [...this.state.alumnos, {nombre: nombre, nota: nota}]});
    }
    menos=(index)=>{
        this.setState({
            alumnos:this.state.alumnos.filter((alumno,i)=>i!=index)
        });
    }

    render() {

        const filas = this.state.alumnos.map((fila, index) => {
            return (<Fila key={index} nombre={fila.nombre} nota={fila.nota} mas={this.mas}/>);
        }
        );
        return (
                <div>
                    <button onClick={() => this.mas("Ana", 6)}>Añadir</button> <button onClick={() => this.menos(0)}>Quitar</button>
                    <table>
                        <thead>
                        <Cabecera/></thead>
                        <tbody>
                      [      {filas}
                        </tbody>
                    </table>
                </div>
                );
    }
}
;
class App extends Component


{
    render() {
        const alumnos = [{nombre: "Ana", nota: 6}, {nombre: "Pep", nota: 4}, {nombre: "Eva", nota: 8}, {nombre: "Ot", nota: 7}];
        return (
                <Tabla alumnos={alumnos}/>
                );
    }
}



;
export default App;

Pasar funciones a componentes hijos

Un ejemplo:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class Cabecera extends Component {
    render() {

        return (
                <tr><th>Alumno</th><th>Nota</th></tr>
                );
    }
}
;
class Fila extends Component {
    render() {
        return (
                <tr onClick={() => this.props.mas(this.props.nombre, this.props.nota)}>
                    <td>{this.props.nombre}</td><td>{this.props.nota}</td>
                </tr>
                );
    }
}
;
class Tabla extends Component {
    constructor(props) {
        super(props);
        this.state = {
            alumnos: this.props.alumnos,
            cont: 0
        };
        this.mas = this.mas.bind(this);
    }
    mas(nombre, nota) {

        this.setState({alumnos: [...this.state.alumnos, {nombre: nombre, nota: nota}]});
    }

    render() {

        const filas = this.state.alumnos.map((fila, index) => {
            return (<Fila key={index} nombre={fila.nombre} nota={fila.nota} mas={this.mas}/>);
        }
        );
        return (
                <div>
                    <button onClick={() => this.mas("Ana", 6)}>Añadir</button>
                    <table>
                        <thead>
                        <Cabecera/></thead>
                        <tbody>
                            {filas}
                        </tbody>
                    </table>
                </div>
                );
    }
}
;
class App extends Component


{
    render() {
        const alumnos = [{nombre: "Ana", nota: 6}, {nombre: "Pep", nota: 4}];
        return (
                <Tabla alumnos={alumnos}/>
                );
    }
}



;
export default App;

Ver círculo (II)

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class Circulo extends React.Component {
    render() {
        var circuloStyle = {
            padding: 10,
            margin: 20,
            display: "inline-block",
            backgroundColor: this.props.bgColor,
            borderRadius: "50%",
            width: 100,
            height: 100,
        };
        return (
                <div style={circuloStyle}>
                </div>
                );
    }
}

function verCirculo(num) {
    let colores = ["#393E41", "#E94F37", "#1C89BF", "#A1D363", "#FF2178","#e5d8bf","#00818a","#f54291","#ff0000"];

    let circulos = [];
    for (let i = 0; i < num; i++) {
        let ran = Math.floor(Math.random() * colores.length);
        circulos.push(<Circulo key={i} bgColor={colores[ran]} />);
    }
    return circulos;
}

class VerCirculo extends React.Component {
    render() {
        return verCirculo(this.props.num);
    }
}
class App extends Component
{
    constructor(props){
        super(props);
        this.state={cont:0};
    }
    foo=()=>{
        this.setState({cont:this.state.cont+1});
    }
    render() {

        return (
                <div onClick={this.foo}>
                    <VerCirculo num="3"/>   
                    {verCirculo(this.state.cont)} 
                </div>
                );
    }
}


export default App;