React: komponenter

React apps er lavet af komponenter! Hele idéen med react er at skabe lynhurtige, overskuelige, objektorienterede web-apps. Hvis du kigger på en side som Facebook – som jo på sin vis satte react projektet i gang – kan du hurtigt tælle dig frem til at siden har mange forskellige små dele med kompleks funktionalitet. Feed, venner, begivenheder, beskeder osv osv – og det hele skal loade lynhurtigt med real time data.

Hvordan gør man det? Jo man laver en react app baseret på små komponenter, som løser og renderer al den funktionalitet siden indeholder – men lidt af gangen og hver for sig.

En komponent er en klasse

Så lad os prøve at lave en simpel komponent. Noget af koden minder en hel del om det JSX vi så på i sidste øvelse – men der anvendes også metoder fra objektorienteret programmering, som er gode at kende.

Vi vil ikke gå i dybden med grundlæggende objektorienteret programmering her, men start med følgende huskeregler:

  • Objekter er “klasser”, dvs en samling variabler og metoder som kan beskrive forskellige entiteter i et program.
  • Man kan “instantiere” uendeligt mange nye kopier af en objektklasse, kaldet “instanser”
  • Instanser har grundlæggende egne variable og metoder, men kan dels gøre dem offentlige/læsbare udefra og somme tider også bruge globalt scope
  • Klasser kan “nedarve” fra hinanden, man kan altså have en “superklasse” der indeholder de mest generelle træk, og “subklasser” der er mere specifikke

Betragt følgende react/JSX script:

import React from 'react';
import ReactDOM from 'react-dom';

class MyComponentClass extends React.Component {
  render() {
    return <h1>Hello world</h1>;
  }
};

ReactDOM.render(
  <MyComponentClass />,
  document.getElementById('app')
);

The React and React DOM library

De to første linjer importerer kort fortalt to biblioteker ind i to variable, React og ReactDOM. Det første bibliotek er selve react-koden – uden den kan JSX eksempelvis ikke kompileres.

Det næste er ‘react-dom’ biblioteket – som giver os metoden ReactDom.render() – hvormed vi kan sætte kompileret JSX kode ind i HTML DOM.

A component class

Fra linje 4 oprettes der en meget simpel objektklasse “MyComponentClass”. Der står at klassen “extends React.component” og det vil netop sige, at den arver sin struktur fra en superklasse.

En react klasse SKAL

  • navngives efter konventionen stort forbogstav, ingen mellemrum, sammensatte ord med store forbogstaver
  • implementere en render() metode

Inde i klassen er der kun et udtryk, eller en funktion – render(){}, som øjensynligt returnerer noget JSX. Render() er netop en metode som klassen har “arvet” fra superklassen, derfor kan den deklareres uden fx at skrive function foran.

Nu er klassen eller komponenten sådan set klar til at blive brugt. Den kan kun returnere noget statisk JSX, men det bliver selvfølgelig meget mere morsomt hen af vejen.

I linje 10 kaldes ReactDOM.render(), som er den grundlæggende react metode til at rendere komponenter til HTML. Metoden tager to argumenter:

  • <MyComponentClass /> leder efter en klasse med navnet “MyComponentClass” og hvis den finder den, instantierer den en ny instans af klassen MyComponentClass
    og kaldes dens render metode – der altså her returnerer JSX.
  • document.getElementById(‘app’) leder i selve HTML dokumentets DOM efter et element med id=”app” – og sætter herefter kompileret JSX ind i DOM’en.

Tip
A component class is like a factory that builds components. It builds these components by consulting a set of instructions, which you must provide. There is only one property that you have to include in your instructions: a render method.
Code Academy

Components can render other components!

This is cool – because in HTML architecture it can often be quite a hassle to make bits and pieces function across pages. This is where frameworks like React begin to make some sense. Let’s imagine we have a website with different subpages, but a navigation bar on all pages. Basically the strategy in React is of course to make the navigation bar as a component class, and the let the subpage components render it.

We start of with some default navigation bar component. Make this component in a separate file, NavBar.js.

Remark that the component omits importing the react-dom library – we leave rendering to other components that need the navigation bar:

import React from 'react';

class NavBar extends React.Component {
  render() {
    const pages = ['home', 'blog', 'pics', 'bio', 'art', 'shop', 'about', 'contact'];
    const navLinks = pages.map(page => {
      return (
        <a href={'/' + page}>
          {page}
        </a>
      )
    });

    return <nav>{navLinks}</nav>;
  }
}

Now, let’s take a look at a subpage component, i.e a profile page. This component now needs to do two things in order to render the NavBar component.

  • import the NavBar component: import { NavBar } from ‘./NavBar.js’
  • render the component wherever suitable: <NavBar />
import React from 'react';
import ReactDOM from 'react-dom';
import { NavBar } from './NavBar.js'

class ProfilePage extends React.Component {
  render() {
    return (
      <div>
				<NavBar />
        <h1>Dr. Profile Page</h1>
        <p>I'm a doctor and so on...</p>
        <img src="https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-monkeyselfie.jpg" />
      </div>
    );
  }
}

If you use an import statement, and the string at the end begins with either a dot or a slash, then import will treat that string as a filepath. import will follow that filepath, and import the file that it finds.

Export

Well, actually we need to do three things before we can render our component. Import and export are part of the ES6 ecosystem, and they most often go together. In order to import our component in ProfilePage, we need to export it from NavBar first:

export class NavBar extends React.Component {

Good stuff – we are now completing the task of exporting and importing components – the tool to create complex and amazing web applications through the react framework.

Named exports
There are a few different ways to use export. In this course, we will be using a style called “named exports.” Here’s how named exports works:

In one file, place the keyword export immediately before something that you want to export. That something can be any top-level var, let, const, function, or class.

Props – passing information between components

Information that gets passed from one component to another is called “props.” Every component has something called props – though they may sometimes not be used for anything. Props is an object – to display any component’s props try this:

render() {
  console.log("Props object comin' up!");

  console.log(this.props);

  console.log("That was my props object!");

  return <h1>Hello world</h1>;
}

You can now inspect the default props object in the console – but as you can see, it doesn’t make much sense for now. What we need to do is equip the props object with some information – i.e from another component. So let’s get to work.

Pass ‘props’ to a Component

You pass props to a component, simply by specifying an attribute value when you use it:

  //in sending component
  const names = ['finn', 'per', 'kamma'];
  <ComponentClass propAttribute={names}>
  
  //in receiving component
    const listNames = this.props.names.map(
    	(val, key) => (
    		<li key={key}>{val}</li>
    	)
    )

..where listNames would now output:

  • finn
  • per
  • kamma

Remark that react always demands any list to have a unique ‘key’ value. This is tidy business, but makes sense semantically.

Passing eventhandlers in props

You can pass functions as props!

You define an event handler as a method on the component class, just like the render method. Almost all functions that you define in React will be defined in this way, as methods in a class.

import React from 'react';

class Example extends React.Component {
  handleEvent() {
    alert(`I am an event handler.
      If you see this message,
      then I have been called.`);
  }

  render() {
    return (
      <h1 onClick={this.handleEvent}>
        Hello world
      </h1>
    );
  }
}

So this is the standard way to implement an eventhandler – or any other function in fact. Defined as a method in a class. But what if we wanted to pass the method to another component, say a button? The lingo for the passing, goes pretty much as a normal prop – though with the “this” keyword, to exclaim scope:

<SendMeAMethod handleEvent = {this.handleEvent} />

In the receiving class, using the passed method is straightforward:

export class Button extends React.Component {
  render() {
    return (
      <button onClick={this.props.handleEvent}>
        Click me!
      </button>
    );
  }
}

This is a neat way to have i.e buttons stored in an individual component. Say you wanted to change a class or syntax type or the like on all buttons in a website – with react you jut open the button component.

Component children and props

Until now we have rendered classes as <ClassName /> – this is in fact short for <ClassName></ClassName>. It is fully possible to have content (children) inside a class instance:

<ClassName>
  <h1>I am the very first child of ClassName</h1>
</ClassName>

//and to render these children-props inside the class:
{this.props.children}

You may also have other components as children-props of components. So this is basically how you would easily pass a component to another component:

<SuperComponent>
  <subComponent />
</SuperComponent>

State

We have now seen how to pass information between React components. But this information has been static. In order to use dynamic information across React components, we shall get to know another core concept of the framework: state.

Imagine a weather app. A component that shows weather – i.e temperature – will need to change according to some incoming data. Or any other component that relies on dynamic information.

A React component can access dynamic information in two ways: props and state. Unlike props, a component’s state is not passed in from the outside. A component decides its own state. Writing a components state looks a bit odd at first. To give any component a state, is done by giving it a constructor method directly after the class declaration:

class HaveAState extends React.Component{
	constructor(props){
		super(props)
		this.state = {
			atmos: 'loud'
		}
	}
}

Yes, odd. In fact the constructor method and super keyword, has nothing to do with React in themselves, it is simply part of the modern javascript standard. When you work with object oriented programming (i.e classes and components), you always give a class a constructor method: a method that is called initially when new instances are born.

The term “super” has to do with scope – using super(props) mean the object will inherit props from its ‘superclass’ – React.Component.

Access a component’s state

To read a component’s state, simple use this.state the same way as this.props:

<p>The atmosphere is {this.state.atmos}</p> 

Update state

To update a component’s state, use this.setState(). The method takes two arguments: an object that will update the component’s state, and a callback. You basically never need the callback.

//initial state in constructor
{
  mood:   'great',
  hungry: false
}
//someone calls setState 
this.setState({ hungry: true });

//and the final state would be
{
  mood:   'great',
  hungry: true
}

this.setState() takes an object, and merges that object with the component’s current state. If there are properties in the current state that aren’t part of that object, then those properties remain how they were.

This is all good BUT. You would now think that you could implement some eventHandler or method to set a components state right away, like this:

handleClick(){
	this.setState(
		{
		  	mood:   'terrible',
  			hungry: true			
		}
	)
}

This will not work out of the box! You will first have to get back to the constructor, and bind your method to the components state:

class HaveAState extends React.Component {
  constructor(props) {
    super(props);
    this.state = { weather: 'sunny' };
    this.handleClick = this.handleClick.bind(this);
  }

This is definitely getting weird and very object oriented high level stuff. You don’t really need to go there at this point, but if you can’t handle the pressure, follow the link in the following tooltip box:

For an in-depth explanation of this kind of binding magic, begin with the React docs. For the less curious, just know that in React, whenever you define an event handler that uses this, you need to add this.methodName = this.methodName.bind(this) to your constructor function.

Anyway, having done this, you are able to make handleClick apply your component state values with setState. There is one really cool thing about setState that almost make it worth all the trouble: Any time that you call this.setState(), this.setState() AUTOMATICALLY calls .render() as soon as the state has changed. This means that any state variable used in your component (i.e a background color, an id, a classname, a form value etc), will actually be changed in the DOM. Good stuff

Update state from child component

Let’s expand a bit on the handling of react state across components. Say we have a child component and want to give that component access to change state of a parent statefull component. Observe the following code:

import React from 'react';
import ReactDOM from 'react-dom';
import { ChildClass } from './ChildClass';

class ParentClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = { totalClicks: 0 };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    const total = this.state.totalClicks;

    // calling handleClick will 
    // result in a state change:
    this.setState(
      { totalClicks: total + 1 }
    );
  }

  // The stateful component class passes down
  // handleClick to a stateless component class:
  render() {
    return (
      <ChildClass onClick={this.handleClick} />
    );
  }
}

The stateless child component can now receive the state handler from the parent class and affect it:

import React from 'react';
import ReactDOM from 'react-dom';

export class ChildClass extends React.Component {
  render() {
    return (
      // The stateless component class uses
      // the passed-down handleClick function,
      // accessed here as this.props.onClick,
      // as an event handler:
      <button onClick={this.props.onClick}>
        Click Me!
      </button>
    );
  }
}

Passing state function parameters between parent/child

Say you have a more complex state variable, and you need to pass a variable from the child class. In this case, you have to define a new method in the child class in order to call the parent function with parameters.

Parent:

handleState(val){
	this.setState({
    	col: val,
    });
}
render(
  <ChildClass onChange={this.handleState} />
)

Child:

export class ChildClass extends React.Component {
  constructor(props){
    super(props)
  	this.handleChange = this.handleChange(this)  
  }
  handleChange(e){
  	//e points to the element passed from select
    const chosenVal = e.target.value;
	//this.onChange points to the state method in the parent class  
    this.props.onChange(chosenVal);
  }
  render() {
    return (
      <select id='colorSelect' onChange={this.handleChange}>
		<option value="orange">Orange</option>
		<option value="apple">Apple</option>
		<option value="lemon">Lemon</option>
      </select>
    );
  }
}

Styling

Inner styles

One funny thing that you will often see in react, is double curly braces {{…}}. An inline style is a style that’s written as an attribute, like this:

<h1 style={{ color: 'green' }}>Hello world</h1>

The outer curly braces inject JavaScript into JSX. They say, “everything between us should be read as JavaScript, not JSX.”

The inner curly braces create a JavaScript object. They make this a valid JavaScript object:

{ color: 'green', padding: 20px }

Style objects

Another option is to define an object variable, and then inject it later:

import React from 'react'
import ReactDOM from 'react-dom'

const styles = {
	background: 'lightblue',
  	color:	'pink',  
  	marginTop: 100,
  	fontSize:'50px'
}

const styled = <h1 styles={styles}>Hey, I'm stylish</h1>
      
ReactDOM.render(
	styleMe, 
	document.getElementById('app')
);      

Remark that React styles are written in camelCase letters. Besides from that they are completely similar to the standard css names. Also note that react assumes “px” unless else is denoted, so if you want to use pixels in your style declaration, just use a number (as with marginTop in the above example).

Exporting styles

Say we have a set of styles that we want to be able to use in different components. For that purpose we can create a JSX style file, and then export / import it across our application.

const fontFamily = "Times New Roman";
const padding = 50;

export const stylesExport = {
    fontFamily: fontFamily,
    padding: padding,
    color:'white',
}

Now all we need to do in order to use this generic style object, is to import it in a class component:

import React from 'react'
import { stylesExport } from './stylesExport';

export class StyledClassImport extends React.Component{
    render(){
        return(
            <div style={stylesExport} className="styledClass">
                <h2>This is a class using an imported styles object</h2>
            </div>
        )
    }
}

React and API’s

Let’s have a look at how we can import data from an API into React. This is in fact where React begins to make some actual sense in terms of work.

Giphy api

We could choose between a wealth of different API data sources. But we will get back to that. For now, head over to giphy api and get an api key – you need that to access the api, as you will see soon.

You will need to create an ‘app’ in order to get your api key:

API’s has endpoints – basically URL’s that direct you to certain parts of the databank. For this example we want to use Giphy’s search endpoint: api.giphy.com/v1/gifs/search

You can see all queryparameters below. What we want is a simple search and, for now, just a single image in return. We can ask for that using the following URL:

http://api.giphy.com/v1/gifs/search?api_key=the_api_key_you_just_got&q=some_phrase&limit=1
api_key: string(required)YOUR_API_KEYGIPHY API Key.
q: string(required)cheeseburgersSearch query term or phrase.
limit: integer (int32)20The maximum number of objects to return. (Default: “25”)
offset: integer (int32)5Specifies the starting position of the results. Defaults to 0.
rating: stringgFilters results by specified rating. If you do not specify a rating, you will receive results from all possible ratings.
lang: stringenSpecify default language for regional content; use a 2-letter ISO 639-1 language code.

Basically, what we will get back is an array with one or more json formatted gif objects in it. If the spec seems hard to read, we will have a look at the console later to see an example of what that might look like.

import React, {Component} from 'react';
import {stdCenter} from './stdCenter';

export class SomeApiClass extends Component {
    constructor(props) {
        super(props);
        this.state = {
            term: '',
            img: ''
        };
    }

    onChange = (event) => {
        this.setState({term: event.target.value});
    }

    handleSubmit = (event) => {
        event.preventDefault();
        const api_key = 'xLNiiuCTPYTZA4gHLsiuUk67YYS6K4tz';
        const url = `http://api.giphy.com/v1/gifs/search?q=${this.state.term}&api_key=${api_key}&limit=2`;
        fetch(url)
            .then(response => response.json())
            .then(data => this.setState({term: '', img: data.data[0].images.fixed_height.url}))
            .catch(e => console.log('error', e));
    }

    render() {
        return (
            <div className="SomeApiClass" style={{
                stdCenter
            }}>
                <form onSubmit={this.handleSubmit}>
                    <input value={this.state.term} onChange={this.onChange}/>
                    <button>Search!</button>
                </form>
                <img src={this.state.img} height="200" alt={this.state.term}/>
            </div>
        );
    }
}

Let’s find another api

Well. Yes – luckily there’s an api, that allows us to return random api’s with no authorization.

This post was written with inspiration from the excellent codeacademy React tutorial. I highly recommend this course for anyone that wishes to dive deep into this subject.

Leave a comment