Vicente Rodríguez

Oct. 31, 2016

Tutorial de react y webpack con babel

En este tutorial veremos como crear una app en react con la sintaxis de ecmascript 6 y webpack.

Usaremos webpack porque es una forma rápida de convertir ecmascript6 a ecmascript5 ademas de que es una herramienta muy poderosa.

Aqui dejare el repositorio de github.

¿Porque react?

Es una pregunta sencilla, si conoces frameworks como angular que son complejos y extensos pues react es todo lo contrario, es rápido, de fácil aprendizaje y lo usa facebook e instagram.

Estos chicos crearon el framework para resolver la necesidad que tenían de que su data cambiaba todo el tiempo.

Creando la configuración

Esta seria la estructura de directorios

Iniciamos el projecto


npm init

En las opciones que te pide puedes poner lo que gustes.

Y empezamos a instalar las dependencias


npm install babel-loader babel-preset-es2015 babel-preset-react 

babel-preset-stage-0 html-webpack-plugin webpack webpack-dev-server

--save-dev



--save-dev se refiere a que guardara las referencias a los paquetes

en el archivo packaje.json en la sección devDependencies


npm install react react-dom --save

--save guardara las referencias en la sección dependencies

Les recomiendo que vean las versiones en el packaje.json del repositorio de github por si tienen algún error.

Esta seria la configuración de webpack, esta comentada y explicada.


const webpack = require("webpack");

//creamos la constante para guardar webpack

const path = require("path");

//usamos path para manejar mejor las rutas

const srcPath = path.join(__dirname, "src");

// __dirname se refiere a la ubicacion

// donde se encuentra este archivo

// con path join juntamos esta ubicacion con

//la carpeta src

const HtmlWebpackPlugin = require("html-webpack-plugin");

//es un plugin de webpack para poder injectar los js en un archivo html



module.exports = {

 entry: {

  app: path.join(srcPath, "js/index.js")

  //se selecciona el archivo main de donde se importaran los demas js

 },

 output: {

  filename: '[name].js',

  //declara el nombre del archivo,

  //[name] toma el nombre que definamos en entry

  //en este caso app 

  path: path.join(__dirname, "dist")

  //la ruta donde se mandara el archivo js

 },

 module: {

  loaders: [

   {

    test: /\.js?$/,

    exclude: /node_modules/,

    loader: 'babel-loader'

    //los loaders sirven para modificar los archivos

    //en este caso babel para poder pasar la sintaxis de

    //ecmascript6 a ecmascript5 que reconocen los navegadores

    //exclude va a evitar los packetes que no se requieran de esa carpeta

   }

  ]

 },

 resolve: {

  root: srcPath,

  //se elige la ruta padre para evitar tener que poner "../"

  //y salir de la carpeta y hacer un import o require

  //en este caso los imports empezaran desde src

  extensions: [".js", ""],

  //se eligen las extensiones que usara webpack

  modulesDirectories: ['node_modules']

  //se declara el directorio donde estan los modulos

 },

 plugins: [

  new HtmlWebpackPlugin({

   inject: true,

   template: 'src/index.html'

  })

  //en esta seccion se ponen los plugins que hayas instalado

  //en este caso el de html para injectar los js

 ],

 debug: true,

 devtool: "source-map",

 //para que nos genere source maps

 //y puedas debugear sin importar que ya este compilado todo en un archivo

 devServer: {

  contentbase: './dist'

  //la carpeta de donde se serviran los archivos

 }

 //para poder crear un servidor que sirva los archivos

}

Tendremos que crear un archivo llamado .babelrc donde se pondrán las dependencias de babel ya que es su nueva version numero 6 no todas vienen instaladas por default.


{

  "presets": ["es2015", "react", "stage-0"]

}

Se crea al mismo nivel que webpack.config.js

para finalizar en el packaje.json en la sección de scripts creamos dos de estos:


  "scripts": {

    "build": "webpack -p --progress --profile --colors",

    "watch": "webpack-dev-server -hot --progress --colors"

  },

build servirá para crear los archivos de producción y minificarlos

watch iniciara el servidor que creara archivos nuevos cada vez que hagamos algún cambio en los javascripts.

Iniciando con react

En el archivo index.html pondremos un div con este id:


 <div id="content"></div>

El id servirá de referencia a react para saber donde colocar los componentes.

Dentro de la carpeta js creamos un archivo llamado index.js:


import React from 'react';

//import funciona como require

//indicas que quieres importar y de donde

import ReactDOM from 'react-dom';

//reactdom apartir de react 0.14 sirve para poder hacer render

//de los componentes



//las clases son parecidas a las de otros lenguajes

//aqui creamos la clase MainView que hereda de React.Component

//este ya es considerado un componente

class MainView extends React.Component {

 //el metodo render sirve para indicar que elementos tipo html

 //se usaran en el componente

 render() {

  return (

   <div>

    soy un componente

   </div>

   //usa una sintaxis llamada jsx para mayor facilidad

   //en compilacion, se crea un metodo llamado React.createElement(tag)

  )

 }

}



//con ReactDOm hacemos render del componente 

ReactDOM.render(

 <MainView/>,

 //la sintaxis para componentes es con camelcase y con etiqueta de cierre al final

 document.querySelector("#content")

 //indicamos en que parte del html se insertara el componente

);

Ejecutamos npm run watch para iniciar el servidor, vamos a la ruta localhost:8080 para poder ver nuestro componente en ejecución.

Creando mas componentes

En la carpeta components crearemos tres archivos:

commentsBox.js


import React from 'react';

import CommentForm from 'js/components/commentForm';

import CommentList from 'js/components/commentList'

//importamos los otros componentes para poder hacer render de ellos

//dentro del commentBox



//export default le va a decir al import que exporte por defecto esta clase

export default class CommentBox extends React.Component {

 render () {

  // en javascript class es una palabara reservada

  // para poder asignar clases a los elementos html

  //usamos className

  return (

   <div className="commentsBox">

    <h1>Commentarios</h1>

    <CommentList/>

    <CommentForm/>

   </div>

  )

 }

}

commentForm.js


import React from 'react';



export default class CommentForm extends React.Component {

 render() {

  return (

   <div className="commentForm">

    commentForm

   </div>

  )

 }

}

commentList.js


import React from 'react';



export default class CommentList extends React.Component {

 render() {

  return (

   <div className="commentList">

    commentList

   </div>

  )

 }

}

Uso de props

Los props son la forma en la cual los componentes pueden pasarse datos.

Se usa


this.props.nombre

para acceder a una propiedad que paso un componente padre, donde nombre es el nombre de la propiedad.

creamos un nuevo componente llamado Comment

comment.js


import React from 'react';



export default class Comment extends React.Component {

 render() {

  return (

   <div className="comment">

    <h2 className="comment-author">{this.props.author}</h2>

    {this.props.children}

    //para poder ejecutar javascript dentro de jsx se usan las llaves {}

   </div>

  )

 }

}

Modificamos commentList para poder hacer render de nuestro nuevo componente


import React from 'react';

import Comment from 'js/components/comment';

//importamos el componente comment



export default class CommentList extends React.Component {

 render() {

  return (

   <div className="commentList">

    <Comment author="pete hunt">Este es un comentario</Comment>

    //pasamos el nombre del atributo como si fuera una propiedad

    //de un tag de html y le asignamos el valor que queramos

    <Comment author="jordan hunt">Este es otro comentario</Comment>

    //en el caso de this.props.children

    //se usara lo que este adentro del componente

   </div>

  )

 }

}

vamos al navegador y recargamos la pagina para ver nuestros comentarios.

Usando un api para los comentarios

No queremos que los comentarios sean estáticos, por eso usaremos un api que esta disponible para hacer este tipo de pruebas.

primero agregaremos la url, la pasaremos como un prop.

modificamos el componente MainView en el archivo index.js


class MainView extends React.Component {

 render() {

  return (

   <div>

    <CommentBox url="http://jsonplaceholder.typicode.com/comments"/>

   </div>

  )

 }

}

Si queremos usar algo mas dinámico necesitaremos del state, los props son inmutables, solo se pasan por los componente padre y no cambian en la ejecución, los state


this.state

pueden cambiar con this.setState(), cuando se ejecuta esta función el componente vuelve a hacer render.

En ecmascript5 teníamos la función getInitialState

, aqui podemos definir los state del componente.

En ecmascript6 esta función se pasa al constructor de la clase.

Como haremos peticiones al servidor, instalaremos una librería que se llama superagent para poder usar ajax con mayor facilidad y sin tener que requerir de jquery.


npm install superagent --save

Usaremos el método componentDidMount que react llama automáticamente cuando se termina de hacer render del componente, aquí podemos hacer uso de diferentes librerías o hacer peticiones ajax.

modificamos CommentBox


export default class CommentBox extends React.Component {

 constructor(props) {

  super(props);

  this.state = {

   data: []

  }

 }

 loadComents() {

  Promise.resolve(request.get(this.props.url))

   .then((data) => {

    this.setState({data: data.body});

   });

 }

 componentDidMount() {

  this.loadComents();

 }

 render () {

  return (

   <div className="commentsBox">

    <h1>Commentarios</h1>

    <CommentList data={this.state.data}/>

    <CommentForm/>

   </div>

  )

 }

}

Hacemos uso de nuevas funciones de ecmascript6 como son las arrow functions => que son funciones anónimas pero que no se le tienen que hacer un bind(this) porque no pierden el scope de donde se definieron y usamos promesas para hacer la petición y cuando se termine de ejecutar .then cambiar el state del componente.

Modificamos CommentList


import React from 'react';

import Comment from 'js/components/comment';



export default class CommentList extends React.Component {

 render() {

  //con la funcion map iteramos cada elemento y creamos

  //componentes comment pasandole las propiedades y guardadolos

  //en el array comments

  var comments = this.props.data.map((comment) => {

   return (

    <div className="commentList">

     <Comment author={comment.email} key={comment.id}>

      {comment.name}

     </Comment>

    </div>

   )

  });

  //al final hacemos render del array de componentes

  return (

   <div>

    {comments}

   </div>

  )

 }

}

Creando nuestros comentarios

Ya casi terminamos nuestra lista de comentarios pedidos al servidor, pero también es importante saber como crear los nuestros y hacer la petición para enviarlos, como es un api para hacer pruebas no se guardaran los comentarios que enviemos pero si tienes un api propio puedes probarlo.

Modificaremos CommentForm:


import React from 'react';



export default class CommentForm extends React.Component {

 handleSubmit(e) {

  e.preventDefault();

  //impedimos que actue como un form normal

  let author = this.refs.author.value.trim();

  //con this.refs podemos obtener el valor de los campos

  //a los cuales hace referencia

  //trim es un metodo para eliminar los espacios en blanco de 

  //izquiera y derecha

  let text = this.refs.text.value.trim();

  //aqui se ejecutara una funcion que commentbox pasa como prop

  this.props.onCommentSubmit({email: author, name: text});

  //usamos email y name porque son los nombres de los campos que nos da

  //el api



  this.refs.author.value = "";

  this.refs.text.value = "";

  //vaciamos los campos

 }

 render() {

   //en ecmascript6 react no hace bind de los metodos

   //tenemos que especificarle que haga referencia a este scope

   //con la funcion bind(this)

  return (

   <div className="commentForm">

    <form onSubmit={this.handleSubmit.bind(this)}>

     <input type="email" placeholder="Email" ref="author"/>

     <input type="text" placeholder="Comentario.." ref="text" />

     <input type="submit" value="post"/>

    </form>

   </div>

  )

 }

}

y por ultimo agregamos los detalles a commentBox


export default class CommentBox extends React.Component {

 constructor(props) {

  super(props);

  this.state = {

   data: []

  }

 }

 loadComents() {

  Promise.resolve(request.get(this.props.url))

   .then((data) => {

    this.setState({data: data.body});

   });

 }

 handleCommentSubmit(comment) {

  //descomenta esto si tienes algun api para probar

  // Promise.resolve(request.post("url del api")

  // .send({data: comment}))

  // .then((data) => {

  // this.setState(data: data.body)

  // });

  //como el api no nos funciona, simplemente pasaremos el nuevo state

  //a los componentes

  let comments = this.state.data;

  comment.id = Date.now();

  comments = comments.concat(comment);

  this.setState({data: comments});



 }

 componentDidMount() {

  this.loadComents();

 }

 render () {

  // en javascript class es una palabara reservada

  // para poder asignar clases a los elementos html

  //usamos className

  return (

   <div className="commentsBox">

    <h1>Commentarios</h1>

    <CommentList data={this.state.data}/>

    <CommentForm onCommentSubmit={this.handleCommentSubmit.bind(this)}/>

   </div>

  )

 }

}

y listo, tenemos una poderosa app en react.

Si tienes alguna duda o comentario no dudes en contactarme ya sea via twitter o en los comentarios. Gracias por ver el tutorial completo, ya aprendiste algo nuevo, felicidades!