Skip to content

Instantly share code, notes, and snippets.

@djoeman84
Last active January 30, 2018 03:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save djoeman84/bf32b7d65f62a6eb0b5b80676c7a0840 to your computer and use it in GitHub Desktop.
Save djoeman84/bf32b7d65f62a6eb0b5b80676c7a0840 to your computer and use it in GitHub Desktop.
Learning React

Super overview of React!

Key nuggest:

  • React takes a functional programming approach on view rendering.
    • Angular and many others hold on to your data as well and tend to conform more to an MVC type patter, React allows you to write your views as functions (with some caveats)
    • Basically react lets you define "given the state of my data, what does my view look like right now?" as functions. Views are therefore very testable (ie call my component with 5 users with these names, I should see 5 cards with names displayed in header).
  • React has two major ways to define "Components"
    • functions (the pure functional way to write views)
function UserCard({user}) {
  return (
    <div className="user-card">
      <h1>{user.name}</h1>
      <div className={user.loggedIn ? 'logged-in-badge' : 'inactive-badge'} />
    </div>
  );
}
  • classes (when you need access to lifecycle hooks or to store state)
class ClickCounter {
  constructor() {
    this.state = {numClicks: 0};
  }
  componentWillUnmount() {
    alert(`Thanks for playing! You clicked ${this.getPluralSuffix()}`);
  }
  getPluralSuffix() {
    return this.state.numClicks === 1 ? '' : 's';
  }
  render() {
    return (
      <button onClick={() => this.setState({numClicks: this.state.numClicks + 1})}>
        {`You've clicked ${this.state.numClicks} time${this.getPluralSuffix()}!`}
      </button>
    )
  }
}
* I realize this one is a bit contrived, but different patterns use these hooks for different reasons. Sometimes you want to load data when your component mounts, sometimes you want to do something to the child html after the component mounts (think if you want to render a third party graphing library, you'll want to add it in `componentDidMount()` and remove it in `componentWillUnmount()`)
  • Components are not the same thing as html elements. There are two types of things you'll render in react:
    • Native elements (for html defined by a library called ReactDOM, these are the things that end up in chrome, ie, etc. They are div, span, button, etc. For native, they are ListView, Button, TextInput, etc)
    • Custom components (these do not show up in the browser/your app, these are just the functions/classes you or some other library are defining that generally will return native elements, but aren't guaranteed to). (See ReactDOM.js)
  • React is generally transpiled, which just means compiled to another high level language. React's language is called JSX, and it allows you to use xml type syntax inline.
    • One bit of syntax to pay attention to are the "{}". Basically, those switch you from XML mode to Javascript mode, so any valid JS can go in there. That's why you can call Array's map, call a function, divide some numbers, or do anything javascript can do.
    • One thing people often have trouble with is that codebases will often have multiple language transpilations in play, you might see flow + es6 + jsx all smashed together with some extra add ons. Always try https://babeljs.io/repl/ if you get confused.
    • Antipattern alert- if you are calling a function in your JSX, generally that can be expressed as another component. (See functionToComponent.js)
    • In the browser, react does something a bit hacky, if a component name is lowercase, it tries to render that to html, otherwise it tries to use a component. This is because there is no complete set of all valid html tag names and you can actually define them on the fly, and html tags are case insensitive.
      • eg:
        => html div,
        would try to find a component you defined called Div.
      • React native gets around all this craziness by having there be no magic lower case things, and you just have to import all of the components that actually render to the screen.
      • Try this thing out if you ever get confused by syntax https://babeljs.io/repl/
  • Some things that are generally used with react but are not the same thing:
    • https://webpack.js.org/ : Webpack allows us to write browser javascript that looks like its being executed server side. In the browser it'd be way to expensive to do a remote request for every file we want to depend on, so we use webpack to bundle them all up into a big javascript file. It also supports plugins that can modify your code (transpiling, etc)
    • https://babeljs.io/ : Babel allows us to use new/non-standard features (like JSX, newer es6/7/8 syntax that isn't supported by all browsers you may want to target)
    • https://redux.js.org/ : Redux is a state management library, also relies heavily on functional programming ideas. Other similar libraries include Flux.
  • React uses something called a virtual DOM, this extends to react-native as well. It builds up an in memory representation of the state of the view after all of your components have been called, it then updates the html/ios view with that. Later on when your data changes, your views will be called again and they will return new representations of the html/ios view. React will figure out what the differences are between those views and change just what needs to be changed to arrive at the new html representation. Note that this is different than backbone and other templating libraries in that it won't just blow everything out and start over, this allows nice properties like the users's cursor not resetting in their text fields, and is a huge performance boost.

FAQ:

  • When does my view get re-rendered? How can I trigger that?
    • If you are using Redux, you may want to look at https://github.com/reactjs/react-redux - this essentially subscribes to updates on your store, and triggers updates in react for you. If you are using this framework you might not have to think about when you are updated at all.
    • Otherwise, a component will re-render if it is re-rendered by a parent, or if it's own state changes. If you look at the click counter example above, it re-renders whenever the someone clicks on the button.
  • How can I get access to the html elements themselves?
    • All components (native html and ReactComponents) all supply a special prop (reacts name for an argument) called ref. Ref is a function that is called with a reference to the element passed in, and called after unmount with null.
    • (See refExample.js below)
    • Note: refs only work when supplied within classes, not functions.
  • If any component returns or any "{}" evaluates to false or undefined, nothing will render there. This is used often to optionally render something based on data:
    function User({user}) {
      return (
        <div>
          <h1>{user.name}</h1>
          {user.isAdmin && <div>Admin!</div>}
        </div>
      )
    }
    
    • Note on this. JS boolean evaluation is fun times. If you use the "&&" operator, it'll evaluate as the last thing if everything is 'truthy', and the first false thing otherwise. "||" is similar, but it'll evaluate as the first truthy thing or the last thing if everything is falsy, which is why people do const x = something || {}; which evaluates to something if something is defined and truthy, otherwise {}.
function UserList({users}) {
return (
<div> // This is a ReactDOM component that'll turn into an html element
{users.map(user =>
<UserCard
key={user.id}
user={user}
/> // This is a React Component, this will not itself be rendered to html, think of it like a function that returns more html
)}
</div>
);
}
function UserCard({user}) {
return (
<div> // ReactDOM => element
<h1>{user.name}</h1> // ReactDOM => element
</div>
)
}
// Antipattern
function getUserHeader(userName) {
return (
<h1>{userName}</h1>
);
}
function MyComponent({user}) {
return (
<div>
{getUserHeader(user.name)}
</div>
);
}
// Do this instead!!
function UserHeader({name}) {
return (
<h1>{name}</h1>
);
}
function MyComponent({user}) {
return (
<div>
<UserHeader name={user.name} />
</div>
);
}
// Ref of a real html element
class RenderHighcharts {
componentDidMount() {
this.chart = Highcharts.chart(this.el, {...});
}
componentWillUnmount() {
this.chart.destroy();
}
render() {
return (
<div ref={el => this.el = el} />
);
}
}
// Ref on a component (careful not to do this too much, since your views are no longer functional)
class Dropdown {
constructor() {
this.state = {open: false};
}
open() {
this.setState({open: true});
}
render() {
const {title, items} = this.props;
return (
<div>
<button>{title}</button>
{this.state.open &&
<ul>
{items.map(
<li>{item}</li>
)}
</ul>
}
</div>
);
}
}
class SelectUser {
handleDropdownRef(el) {
el.open();
/*
calls method on class above. This makes your views more stateful, and thus a bit harder to test.
I'd advise only doing this for shared UI components that have UI centric state (ie dropdowns with open/closed state),
and not normal data-rich views. (ie don't make a comment section component with a `addLike` method or something,
that should be handled by your state library).
*/
}
render() {
return (
<Dropdown
ref={el => this.handleDropdownRef(el)}
title="Users!"
items={["Bob", "Billy"]}
/>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment