Begin React Workshop

the plan

  • concepts & practise
  • extras
  • build a thing

the hope

  • understand concepts
  • know how to think
  • know how to build

starting out

git clone https://github.com/foundersandcoders/begin_react_workshop.git
cd begin_react_workshop
npm install

git checkout babysteps
mkdir -p build/js

npm run dev

package.json

"scripts": {
    "dev": "npm run serve & npm run live-reload & npm run build:watch",
    "prebuild": "npm run lint",
    "build": "browserify -t reactify --es6 src/js/main.jsx -o build/js/main.js",
    "build:watch": "watchify src/js/main.jsx -t reactify --es6 -o build/js/main.js",
    "lint": "jsxhint src/**/*.jsx",
    "live-reload": "live-reload build/ --port 9081",
    "serve": "http-server -s"
 }

branches

  • master              references & resources
  • babysteps        our journey begins
  • babystepsA      a little helping code
  • kidsteps            more fundamentals
  • appmockup     plan for the app
  • appcomplete   a working version

my first render

var React = require("react");

React.render(
  <div>hi</div>,
  document.getElementById("content")
);

ugh

// JSX

React.render(
  <div>hi</div>,
  document.getElementById("content")
);
// plain js

React.render(
  React.createElement("div", null, "hi"),
  document.getElementById("content")
);

components

  • building blocks of ui
  • logically distinct
  • most fundamental part of react
  • power lies in composition

components

var React = require("react");

var UserName = React.createClass({
  render: function() {
    var name = "anon";
    return (
      <div>
        <h1>{name}</h1>
      </div>
    );
  }
});

React.render(<UserName />, document.getElementById("content"));

props

  • one of two ways to customise components
  • external circumstances
  • immutable parameters
  • passed like attributes

props 1

var React = require("react");

var UserName = React.createClass({
  render: function() {
    return (
      <h1>{this.props.name}</h1>
    );
  }
});

var UserStatus = React.createClass({
  render: function() {
    return (
      <p>{this.props.content}</p>
    );
  }
});

props 2

var LikeButton = React.createClass({
  getDefaultProps: function() {
      return {
          liked: false
        };
    },

  render: function() {
    var buttonClass = "like";

    if(this.props.liked) {
      buttonClass += " yup";
    }

    return (
      <button className={buttonClass}>
        S
      </button>
    );
  }
});

props 3

var User = React.createClass({
  render: function() {
    return (
      <div className="app">
        <UserName name="anon" />
        <LikeButton liked={true} />
        <UserStatus content="something about the election" />
      </div>
    );
  }
});

React.render(<User />, document.getElementById("content"));

other stuff

  • getDefaultProps() 
  • Components can be used in render()
  • one root node - nest other components
  • self-closing JSX
  • className rather than class 
  • subtracting "yup"?

state

  • the other way to customise components
  • internal & private
  • dynamic
  • setState() merges & triggers re-render
  • getInitialState()

state

var LikeButton = React.createClass({
  getInitialState: function() {
    return {
      liked: false
    };
  },
  clickHandler: function(e) {
    this.setState({
      liked: !this.state.liked
    });
  },
  render: function() {
    var buttonClass = "like";

    if(this.state.liked) {
      buttonClass += " yup";
    }

    return (
      <button className={buttonClass} onClick={this.clickHandler}>
        {this.state.liked ? "-" : "+"}
      </button>
    );
  }
});

event handlers

  • inline in JSX
  • event delegation
  • single top-level listener
  • e
  • camelCase
  • partial application
onClick={this.clickHandler.bind(null, "first argument")}

higher level state

  • common parent
  • functions as props

state v2.1

var React = require("react");

var UserName = React.createClass({
  render: function() {
    return (
      <h1>{this.props.name}</h1>
    );
  }
});

var UserStatus = React.createClass({
  render: function() {
    return (
      <p>{this.props.content + (this.props.liked ? " :)" : " :(")}</p>
    );
  }
});

state v2.2

var LikeButton = React.createClass({

  clickHandler: function(e) {
    this.props.toggleLike();
  },

  render: function() {
    var buttonClass = "like";

    if(this.props.liked) {
      buttonClass += " yup";
    }

    return (
      <button className={buttonClass} onClick={this.clickHandler}>
        {this.props.liked ? "-" : "+"}
      </button>
    );
  }
});

state v2.3

var User = React.createClass({
  getInitialState: function() {
    return {
      liked: false
    };
  },

  toggleLike: function() {
    this.setState({
      liked: !this.state.liked
    });
  },

  render: function() {
    return (
      <div className="app">
        <UserName name="anon" />
        <LikeButton liked={this.state.liked} toggleLike={this.toggleLike} />
        <UserStatus liked={this.state.liked} content="something about the election" />
      </div>
    );
  }
});

React.render(<User />, document.getElementById("content"));

dynamic kids

  • arrays of components
  • keys keys keys!

dynamic kids

var ListItem = React.createClass({
  render: function() {
    return <li>{this.props.content}</li>;
  }
});

var ListHolder = React.createClass({
  getInitialState: function() {
    return {
      arrayOfData: ["hi", "how", "are", "you"]
    }
  },
  render: function() {
    var arrayOfComponents = this.state.arrayOfData.map(function(e,i) {
      return (
        <ListItem key={i} content={e} />
      );
    });

    return (
      <ul>
        {arrayOfComponents}
      </ul>
    );
  }
});

refs

  • like ids for components
  • React.findDOMNode()

refs

var React = require("react");

var RefExample = React.createClass({
  componentDidMount: function() {
    console.log(this.refs.someOtherRef);
    console.log(React.findDOMNode(this.refs.textinput).value);
  },

  render: function() {
    return (
      <div>
        <SomeOtherComponent ref="someOtherRef" />
        <input type="text" value="hello" ref="textinput" />
      </div>
    );
  }
});

React.render(
  <RefExample />,
  document.getElementById("content")
);

forms

  • uncontrolled?
  • controlled?

uncontrolled

var uncontrolledFormComponent = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();
    var textInputValue = React.findDOMNode(this.refs.textInput).value;
    doSomethingElse(textInputValue);
  },

  render: function() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input type="text" defaultValue="Hello!" ref="textInput" />
        <input type="button" onSubmit={this.handleSubmit}/>
      </form>
    );
  }
});

controlled

var controlledFormComponent = React.createClass({
  getInitialState: function() {
    return {value: "Hello friends"};
  },

  handleChange: function(event) {
    this.setState({value: event.target.value});
  },

  render: function() {
    var value = this.state.value;
    return <input type="text" value={value} onChange={this.handleChange} />;
  }
});

PropTypes

  • validation
  • debugging
  • ease-of-reading
  • disabled in production

validation - proptypes

var Props = React.PropTypes

React.createClass({
  propTypes: {
    optionalArray: Props.array,
    optionalEnum: Props.oneOf(['News', 'Photos']),
    optionalArrayOf: Props.arrayOf(Props.number),
    optionalObjectWithShape: Props.shape({
      color: Props.string,
      fontSize: Props.number
    }),
    requiredFunc: Props.func.isRequired
});
    

Begin React Workshop

By James Griffiths

Begin React Workshop

  • 1,034