diff --git a/README.markdown b/README.markdown index 568cb20..74983d4 100644 --- a/README.markdown +++ b/README.markdown @@ -1,95 +1,83 @@ ## Contents -* [Stateless function](#stateless-function) -* [JSX spread attributes](#jsx-spread-attributes) -* [Destructuring arguments](#destructuring-arguments) -* [Conditional rendering](#conditional-rendering) -* [Children types](#children-types) -* [Array as children](#array-as-children) -* [Function as children](#function-as-children) -* [Render callback](#render-callback) -* [Children pass-through](#children-pass-through) -* [Proxy component](#proxy-component) -* [Style component](#style-component) -* [Event switch](#event-switch) -* [Layout component](#layout-component) -* [Container component](#container-component) -* [Higher-order component](#higher-order-component) -* [Prop hoisting](#prop-hoisting) - -## Stateless function - -[Stateless functions](https://facebook.github.io/react/docs/components-and-props.html) are a brilliant way to define highly reusable components. They don't hold `state` or `refs`; they're just functions. - -```js -const Greeting = () =>
Hi there!
-``` - -They get passed `props` and `context`. - -```js -const Greeting = (props, context) => -
Hi {props.name}!
-``` - -They can define local variables, where a function block is used. - -```js -const Greeting = (props, context) => { - const style = { - fontWeight: "bold", - color: context.color, - } - - return
{props.name}
+- [Functional component](#functional-component) +- [JSX spread attributes](#jsx-spread-attributes) +- [Destructuring arguments](#destructuring-arguments) +- [Conditional rendering](#conditional-rendering) +- [Children types](#children-types) +- [Array as children](#array-as-children) +- [Function as children](#function-as-children) +- [Render callback](#render-callback) +- [Children pass-through](#children-pass-through) +- [Proxy component](#proxy-component) +- [Style component](#style-component) +- [Event switch](#event-switch) +- [Layout component](#layout-component) +- [Container component](#container-component) +- [Higher-order component](#higher-order-component) +- [State hoisting](#state-hoisting) +- [Controlled input](#controlled-input) + +## Functional component + +[Functional components](https://reactjs.org/docs/components-and-props.html#functional-and-class-components) are functions that render UI. +They don't hold state. + +```js +function Greeting() { + return Hi there!; } ``` -But you could get the same result by using other functions. +They are a product of their `props`—which are recieved as an argument. ```js -const getStyle = context => ({ - fontWeight: "bold", - color: context.color, -}) - -const Greeting = (props, context) => -
{props.name}
+function Greeting(prop) { + return Hi {props.name}!; +} ``` -They can have defined `defaultProps`, `propTypes` and `contextTypes`. +`defaultProps` can be added to stand in for props that haven't been provided. ```js -Greeting.propTypes = { - name: PropTypes.string.isRequired +function Greeting(prop) { + return Hi {props.name}!; } + Greeting.defaultProps = { name: "Guest" -} -Greeting.contextTypes = { - color: PropTypes.string -} -``` +}; +// =>
Hi Guest!
+``` ## JSX spread attributes -Spread Attributes is a JSX feature. It's syntactic sugar for passing all of an object's properties as JSX attributes. +[Spread attributes](https://reactjs.org/docs/jsx-in-depth.html#spread-attributes) is a JSX feature that allows pass all of an object's properties as props. These two examples are equivalent. -```js + +```jsx // props written as attributes -
{children}
+
+
Some Content
+
// props "spread" from object -
+let attrs = { + className: "main", + role: "main", + children:
Some Content
+} +
``` -Use this to forward `props` to underlying components. +Use spread attributes to forward `props` to underlying components. -```js -const FancyDiv = props => -
+```jsx +function FancyDiv(props) { + return
; +} ``` Now, I can expect `FancyDiv` to add the attributes it's concerned with as well as those it's not. @@ -112,49 +100,43 @@ We can make `FancyDiv`s className always "win" by placing it after the spread pr ```js // my `className` clobbers your `className` -const FancyDiv = props => -
+const FancyDiv = props =>
; ``` You should handle these types of props gracefully. In this case, I'll merge the author's `props.className` with the `className` needed to style my component. ```js -const FancyDiv = ({ className, ...props }) => -
+const FancyDiv = ({ className, ...props }) => ( +
+); ``` - ## destructuring arguments [Destructuring assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) is an ES2015 feature. It pairs nicely with `props` in Stateless Functions. These examples are equivalent. + ```js -const Greeting = props =>
Hi {props.name}!
+const Greeting = props =>
Hi {props.name}!
; -const Greeting = ({ name }) =>
Hi {name}!
+const Greeting = ({ name }) =>
Hi {name}!
; ``` The [rest parameter syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters) (`...`) allows you to collect all the remaining properties in a new object. ```js -const Greeting = ({ name, ...props }) => -
Hi {name}!
+const Greeting = ({ name, ...props }) =>
Hi {name}!
; ``` In turn, this object can use [JSX Spread Attributes](#jsx-spread-attributes) to forward `props` to the composed component. ```js -const Greeting = ({ name, ...props }) => -
Hi {name}!
+const Greeting = ({ name, ...props }) =>
Hi {name}!
; ``` Avoid forwarding non-DOM `props` to composed components. Destructuring makes this very easy because you can create a new `props` object **without** component-specific `props`. - ## conditional rendering You can't use regular if/else conditions inside a component definition. [The conditional (ternary) operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) is your friend. @@ -162,39 +144,43 @@ You can't use regular if/else conditions inside a component definition. [The con `if` ```js -{condition && Rendered when `truthy` } +{ + condition && Rendered when `truthy`; +} ``` `unless` ```js -{condition || Rendered when `falsey` } +{ + condition || Rendered when `falsey`; +} ``` `if-else` (tidy one-liners) ```js -{condition - ? Rendered when `truthy` - : Rendered when `falsey` +{ + condition ? ( + Rendered when `truthy` + ) : ( + Rendered when `falsey` + ); } ``` `if-else` (big blocks) ```js -{condition ? ( - - Rendered when `truthy` - -) : ( - - Rendered when `falsey` - -)} +{ + condition ? ( + Rendered when `truthy` + ) : ( + Rendered when `falsey` + ); +} ``` - ## Children types React can render `children` of many types. In most cases it's either an `array` or a `string`. @@ -202,17 +188,13 @@ React can render `children` of many types. In most cases it's either an `array` `string` ```js -
- Hello World! -
+
Hello World!
``` `array` ```js -
- {["Hello ", World, "!"]} -
+
{["Hello ", World, "!"]}
``` Functions may be used as children. However, it requires [coordination with the parent component](#render-callback) to be useful. @@ -221,11 +203,12 @@ Functions may be used as children. However, it requires [coordination with the p ```js
- {() => { return "hello world!"}()} + {(() => { + return "hello world!"; + })()}
``` - ## Array as children Providing an array as `children` is a very common. It's how lists are drawn in React. @@ -233,35 +216,25 @@ Providing an array as `children` is a very common. It's how lists are drawn in R We use `map()` to create an array of React Elements for every value in the array. ```js -
    - {["first", "second"].map((item) => ( -
  • {item}
  • - ))} -
+
    {["first", "second"].map(item =>
  • {item}
  • )}
``` That's equivalent to providing a literal `array`. ```js -
    - {[ -
  • first
  • , -
  • second
  • , - ]} -
+
    {[
  • first
  • ,
  • second
  • ]}
``` This pattern can be combined with destructuring, JSX Spread Attributes, and other components, for some serious terseness. ```js
    - {arrayOfMessageObjects.map(({ id, ...message }) => + {arrayOfMessageObjects.map(({ id, ...message }) => ( - )} + ))}
``` - ## Function as children Using a function as `children` isn't inherently useful. @@ -281,7 +254,7 @@ See [Render callbacks](#render-callback), for more details. Here's a component that uses a Render callback. It's not useful, but it's an easy illustration to start with. ```js -const Width = ({ children }) => children(500) +const Width = ({ children }) => children(500); ``` The component calls `children` as a function, with some number of arguments. Here, it's the number `500`. @@ -289,9 +262,7 @@ The component calls `children` as a function, with some number of arguments. Her To use this component, we give it a [function as `children`](#function-as-children). ```js - - {width =>
window is {width}
} -
+{width =>
window is {width}
}
``` We get this output. @@ -304,57 +275,44 @@ With this setup, we can use this `width` to make rendering decisions. ```js - {width => - width > 600 - ?
min-width requirement met!
- : null - } + {width => (width > 600 ?
min-width requirement met!
: null)}
``` If we plan to use this condition a lot, we can define another components to encapsulate the reused logic. ```js -const MinWidth = ({ width: minWidth, children }) => - - {width => - width > minWidth - ? children - : null - } - +const MinWidth = ({ width: minWidth, children }) => ( + {width => (width > minWidth ? children : null)} +); ``` - Obviously a static `Width` component isn't useful but one that watches the browser window is. Here's a sample implementation. ```js class WindowWidth extends React.Component { constructor() { - super() - this.state = { width: 0 } + super(); + this.state = { width: 0 }; } componentDidMount() { this.setState( - {width: window.innerWidth}, - window.addEventListener( - "resize", - ({ target }) => - this.setState({width: target.innerWidth}) + { width: window.innerWidth }, + window.addEventListener("resize", ({ target }) => + this.setState({ width: target.innerWidth }) ) - ) + ); } render() { - return this.props.children(this.state.width) + return this.props.children(this.state.width); } } ``` Many developers favor [Higher Order Components](#higher-order-component) for this type of functionality. It's a matter of preference. - ## Children pass-through You might create a component designed to apply `context` and render its `children`. @@ -362,7 +320,7 @@ You might create a component designed to apply `context` and render its `childre ```js class SomeContextProvider extends React.Component { getChildContext() { - return {some: "context"} + return { some: "context" }; } render() { @@ -375,21 +333,21 @@ You're faced with a decision. Wrap `children` in an extraneous `
` or retu ```js // option 1: extra div -return
{children}
+return
{children}
; // option 2: unhelpful errors -return children +return children; ``` It's best to treat `children` as an opaque data type. React provides `React.Children` for dealing with `children` appropriately. ```js -return React.Children.only(this.props.children) +return React.Children.only(this.props.children); ``` ## Proxy component -*(I'm not sure if this name makes sense)* +_(I'm not sure if this name makes sense)_ Buttons are everywhere in web apps. And every one of them must have the `type` attribute set to "button". @@ -427,19 +385,17 @@ Say we have a button. It uses classes to be styled as a "primary" button. We can generate this output using a couple single-purpose components. ```js -const PrimaryBtn = props => - +import classnames from "classnames"; -const Btn = ({ className, primary, ...props }) => +const PrimaryBtn = props => ; + +const Btn = ({ className, primary, ...props }) => (