React

Pataasin ang iyong marka sa homework at exams ngayon gamit ang Quizwiz!

In a general overview, how might React Router and its techniques differ from more traditional JavaScript routers like Backbone's Router?

"Traditional" routers like the ever-popular Backbone.Router establish a predefined set of routes, in which each route defines a series of actions to take when a route is triggered. When combining Backbone.Router with React, you may have to mount and unmount React Components when the route changes: var MyRouter = Backbone.Router.extend({ routes: { 'home': 'showHome', 'search/:q': 'showSearch', '*default': 'show404' }, showHome(){ DOM.unmountComponentAtNode(document.body) DOM.render(<Home />, document.body) }, showSearch(q){ DOM.unmountComponentAtNode(document.body) DOM.render(<Search query={q} />, document.body) }, show404(){ DOM.unmountComponentAtNode(document.body) DOM.render(<Error />, document.body) } }) The router exists externally of the React Components, and the VDOM has to mount and unmount potentially frequently, introducing a possible slew of problems. React Router focuses on not just "single-level" routing, but enables - nay, empowers - the creation of HOCs that can "decide for themselves" what to render within them. This is where the advanced HOC implementations can really help simplify a seemingly complex notion. Let's look at using a tiny router to assess some of the beauty of embedding application routers inside React HOCs. Here, we define a Component that wraps it's own routing mechanism (router() not provided here, see universal-utils): // router(routesObject, callback) --> when a route event occurs, we invoke callback() with // the React Element and the props passed via the route params class Router extends Component { constructor(...a){ super(...a) let p = this.props this.state = { routes: p.routes || {}, default: p.default || '/' } this.router = router(this.state.routes, (el, props) => { this.current = el }) this.router.trigger(this.state.default) } render(){ return this.current() } } DOM.render(<Router routes={{ '/': () => <Home/>, '/search/:q': ({q}) => <Search query={q} />, '*': () => <Error /> }}/>, document.body) This Router Component opts for parsing the routes object passed into this.props instead of reading over an array of React Components passed as this.props.children. React Router opts for the latter technique. Need proof? Take a look at this example code provided by React Router's docs: DOM.render( <Router history={browserHistory}> <Route path="/" component={App}> <Route path="about" component={About}/> <Route path="users" component={Users}> <Route path="/user/:userId" component={User}/> </Route> <Route path="*" component={NoMatch}/> </Route> </Router> , document.body) A <Router /> Component has one or more <Route /> Components as items in this.props.children, and <Route />s can have sub-<Route />s. React Router's code recursively walks down the tree of children until there are no more to process, allowing the developer to recursively declare routes in a structure that encapsulates sub-routes, instead of having to implement a Backbone-esque flat list of routes (i.e. "/", "/about", "/users", "/users/:id", etc).

How would you create Higher Order Components (HOCs) in React?

Higher Order Components (HOCs) are the coined term for a custom Component that accepts dynamically provided children. For example, let's make <LazyLoad /> Component that takes child image tags as children, waits until the <LazyLoad /> Component is scrolled into view, and then loads the images they point to in the background (before rendering them to the DOM). An HOC accepts children via props: DOM.render( <LazyLoad> <img src="https://media.giphy.com/media/HhvUpQhBWMtwc/200.gif"/> <img src="https://media2.giphy.com/media/3oEduUDvycvu3GYkdG/200w.gif"/> <img src="https://media0.giphy.com/media/142UITjG5GjIRi/200w.gif" /> </LazyLoad>, document.body) Creating an HOC means handling this.props.children in the Component's code: interactive example can be found at https://goo.gl/ns0B6j class LazyLoad extends Component { constructor(p){ super(p) this.state = { loaded:0 } this._scroll = this._scroll.bind(this) } _scroll(){ let el = DOM.findDOMNode(this) let {top} = el.getBoundingClientRect() let viewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) if(top < (viewportHeight + this.props.top)) { window.removeEventListener('scroll', this._scroll) this.setState({loaded:1}) } } componentDidMount(){ window.addEventListener('scroll', this._trackYPosition) this._scroll() } componentWillUnmount(){ window.removeEventListener('scroll', this._trackYPosition) } render(){ let {children} = this.props, {loaded} = this.state return <div> {loaded && children} </div> } } LazyLoad.defaultProps = { top: 100 } Noting a few things about this code: We set up initial state (this.state = {loaded: 0}) in the constructor(). This will be set to 1 when the parent container is scrolled into view. The render() returns the props.children as child elements when this occurs. Extract the src by using ES6 destructuring, where {props:{src}} creates a variable src with the appropriate value. We used a single componentDidMount() lifecycle method. This is used because on mount, we'd like the component to check if the HOC is visible. The largest function of our component, _scroll(), grabs the HOC Component's DOM element with DOM.findDOMNode() and then gets the elements position. This position is compared to the height of the browser window, and if it is less than 100px from the bottom, then the scroll listener is removed and loaded is set to 1. This technique is called HOC (Higher Order Component) because we pass in elements as this.props.children when we nest those elements inside the container component: <HOC> <div>some</div> <span>children</span> <Props/> </HOC> All of these nested elements (which can be custom components) are nested under <HOC/>, thus HOC's code will be able to access them as this.props.children.

What is the significance of keys in React?

Keys in React are used to identify unique VDOM Elements with their corresponding data driving the UI; having them helps React optimize rendering by recycling existing DOM elements. Let's look at an example to portray this. We have two <TwitterUser> Components being rendered to a page, drawn in decreasing order of followers: ----------- | A - 103 | ----------- ----------- | B - 92 | ----------- Let's say that B gets updated with 105 Twitter followers, so the app re-renders, and switches the ordering of A and B: ----------- | B - 105 | ----------- ----------- | A - 103 | ----------- Without keys, React would primarily re-render both <TwitterUser> Elements in the DOM. It would re-use DOM elements, but React won't re-order DOM Elements on the screen. With keys, React would actually re-order the DOM elements, instead of rendering a lot of nested DOM changes. This can serve as a huge performance enhancement, especially if the DOM and VDOM/React Elements being used are costly to render. Keys themselves should be a unique number or string; so if a React Component is the only child with its key, then React will repurpose the DOM Element represented by that key in future calls to render(). Let's demonstrate this with a simple list of todos rendered with React: Interactive code sample available on Matthew Keas' github. class List extends Component { constructor(p){ super(p) this.state = { items: Array(5).fill(1).map((x,i) => ({id:i})) } } componentDidMount(){ const random = (a,b) => Math.random() < .5 ? -1 : 1 setInterval(() => { this.setState({ items: this.state.items.sort(random) }) }, 20) } render() { let {items} = this.state return <ul> {items.map(item => <li key={item.id}>{item.id}</li>)} </ul> } } DOM.render(<List />, document.body) The setInterval() occurring on mount reorders the items array in this.state every 20ms. Computationally, if React is reordering the items in state, then it would manipulate the DOM elements themselves instead of "dragging" them around between positions in the <ul>. It is worth noting here that if you render a homogenous array of children - such as the <li>'s above - React will actually console.warn() you of the potential issue, giving you a stack trace and line number to debug from. You won't have to worry about React quietly breaking.

Compare and contrast incorporating mixins and enforcing modularity in React Components. (extend, createClass and mixins, HOCs) Why would you use these techniques, and what are the drawbacks of each?

Modularity is - in effect - something partially done with intention while coding, and partially done when refactoring afterwards. Let's first paint a scenario which we'll model using each method above. Imagine we have three React Components: onScrollable, Loadable, and Loggable. an onScrollable Component will listen to the window.onscroll event, and use a logging mechanism to record it a Loadable Component will not render until one or more async requests have finished loading, and will use a logging mechanism to record when this occurs a Loggable Component provides a logging mechanism, be it a console, a Winston Node.js logging setup on our own server, or some 3rd party logging service that records logs via JSON requests First, let's model this with React's ES5 API and mixins. Interactive code sample at Matthew Keas' github. var onKeypress = { componentDidMount(){ this.onpress && window.addEventListener('keyup', this.onpress) }, componentWillUnmount(){ this.onpress && window.removeEventListener('keyup', this.onpress) } } var Loadable = { componentDidMount(){ if(this.load){ this.setState({loaded: false}) Promise.all([].concat(this.load)) .then(() => this.setState({loaded: true})) } } } var Loggable = { log(...args) { alert(args) } } var Example = React.createClass({ mixins: [Loggable, Loadable, onKeypress], componentWillMount(){ this.onpress = (e) => this.log(e.which, 'pressed!') this.load = [new Promise((res,rej) => setTimeout(res, 3000))] this.log = (...args) => console.log(...args) }, getInitialState(){ return {} }, render() { if(!this.state.loaded) return <div>loading...</div> return <div>test</div> } }) DOM.render(<Example />, document.body) Let's note a few things about the above code: There are three POJOs (Plain ol' JS Objects) created, which hold lifecycle and/or custom methods. When creating the Example Component, we add mixins: [Loggable, Loadable, onKeypress], meaning that any functions from all three objects are included in the Example class. Both onKeypress and Loadable add a componentDidMount(), but this doesn't mean the latter cancels out the prior. In fact, all componentDidMount() functions from each mixin will be invoked when the event occurs. The same is true for all lifecycle methods added to mixins. This way, both the onKeypress and Loadable mixins will work simultaneously! Mixins are possible, but not built-in to React's ES6 API. However, the ES6 API makes it easier to create a custom Component that extends another custom Component. So our Components' prototype chains would look like the following: [Example] --- extends ---> [Loggable] --- extends ---> [Loadable] --- extends ---> [onKeypress] This would result from Components written as such: class onKeypress {} class Loadable extends onKeypress {} class Loggable extends Loadable {} class Example extends Loggable {} Creating anonymous classes would help here, because then Loggable would not have to extend Loadable and onKeypress. class Example extends (class a extends Loggable extends ...) { } With a mixin() function, this could look more like: class Example extends mixin(Loggable, Loadable, onKeypress) { } Let's try to write mixin() by building a chain of anonymous classes that extend Loggable, Loadable, and onKeypress: const mixin = (...classes) => classes.reduce((a,v) => { return (class temp extends a) }, (class temp {})) There's a caveat, though - if Loadable extends onKeypress and both implement componentDidMount(), Loadable's version will be lower on the prototype chain, which means the function from onKeypress will never be invoked. The takeaway here is that the mixin pattern isn't easily implemented by relying only on the ES6 extends approach. Let's try to implement mixin() again, but build a more robust function: const mixin = (...classes) => { class _mixin {} let proto = _mixin.prototype classes.map(({prototype:p}) => { Object.getOwnPropertyNames(p).map(key => { let oldFn = proto[key] || (() => {}) proto[key] = (...args) => { oldFn(...args) return p[key](...args) } }) }) return _mixin } This new mixin() implementation maps over each class, and cascades function calls from a parent class's componentDidMount() alongside the child's componentDidMount(). There are similar implementations of mixin() available on npm, using packages like react-mixin and es6-react-mixins. We use mixin() from above like so: interactive code sample available at https://goo.gl/VnQ21R class A { componentDidMount(){ console.log(1) } } class B { componentDidMount(){ console.log(2) } } class C extends mixin(A,B) { componentDidMount(...p){ super.componentDidMount(...p) console.log(3) } } let c = new C() c.componentDidMount() // logs 1, 2, 3 Recently, React provided support for - and documented its preference of - React Components declared with ES6 classes. ES6 classes allow us to create component heirarchies with less code, however this makes it more difficult to create a single Component that inherits properties from several mixins, instead forcing us to create prototype chains.

Explain the Virtual DOM, and a pragmatic overview of how React renders it to the DOM.

The Virtual DOM is an interesting concept; it's a complex idea that boils down into a much simpler algorithm. In React, if we create simple ES6 class and print it out, we have a function (as all functions can be used as a constructor in JavaScript): const app = () => { let React = react, {Component} = React, DOM = reactDom class Comments extends Component { constructor(props){ super(props) } render(){ return <div>test</div> } } console.log(Comments) } require('react', 'react-dom').then(app) The console.log(Comments) gives us something that looks like this (after compiled by Babel from ES6 to ES5): function Comments(props) { _classCallCheck(this, Comments); return _possibleConstructorReturn(this, Object.getPrototypeOf(Comments).call(this, props)); } When we write something to draw a React Component to the screen, we might have something like the following: DOM.render(<Comments />, document.body) The JSX gets transpiled into ES5 by Babel as well: DOM.render(React.createElement(Comments, null), document.body); We can see that <Comments /> is transpiled directly into React.createElement(Comments, null). This is where we can see what a Virtual DOM object actually is: a plain JavaScript Object that represents the tag to be rendered onto the screen. Let's inspect the output of React.createElement(): console.log(<div/>) // or console.log(React.createElement('div', null)) This gives us: {"type":"div","key":null,"ref":null,"props":{},"_owner":null,"_store":{}} See how the type is a string? DOM.render({...}) gets this object above and looks at the type, and decides whether or not to reuse an existing <div> element on the DOM or create a new <div> and append it. The Virtual DOM is not a simple Object - it is a recursive structure. For example, if we add two elements beneath the <div/>: console.log(<div><span/><button/></div>) // or console.log(React.createElement( 'div', null, React.createElement('span', null), React.createElement('button', null) )) What we get is a nested Object-tree: { "type":"div", "key":null, "ref":null, "props":{ "children": [ {"type":"span","key":null,"ref":null,"props":{}}, {"type":"button","key":null,"ref":null,"props":{}} ] } } This is why, in a React Component's code, we can access the child and ancestor elements via this.props.children. What React will do is walk down a very deep tree of nested Objects (depending on your UI complexity), each sitting in their parent element's children. One thing to note is that the type so far has just been a string. When a React Element is made from a custom Component (like Comments above), the type is a function: console.log(<Comments />) // or console.log(React.createElement(Comments, null)) gives us: { "key":null, "ref":null, "props":{}, "type": function Component() { ... } } You can play around with a web version of this code at Matthew Keas' github.

Explain the standard JavaScript toolchain, transpilation (via Babel or other compilers), JSX, and these items' significance in recent development. What sort of tools might you use in the build steps to optimize the compiled output React code?

The bleeding edge JavaScript toolchain can seem quite complex, and it's very important to feel confident in the toolchain and to have a mental picture of how the pieces fit together. There are a couple primary pillars in the JavaScript toolchain: Dependency Management, Linting, Style-checking, Transpilation, and Compilation, Minification, Source-Mapping. Typically, we use build tools like Gulp, Watchify/Browserify, Broccoli, or Webpack to watch thea filesystem for file events (like when you add or edit a file). After this occurs, the build tool is configured to carry out a group of sequential or parallel tasks. This part is the most complex piece, and is the center of the development process. The rest of the tools belong in that group of sequential or parallel tasks: Style linting - typically a linter like JSCS is used to ensure the source code is following a certain structure and style Dependency Management - for JavaScript projects, most people use other packages from npm; some plugins exist for build systems (e.g. Webpack) and compilers (e.g. Babel) that allow automatic installation of packages being imported or require()'d Transpilation - a specific sub-genre of compilation, transpilation involves compiling code from one source version to another, only to a similar runtime level (e.g. ES6 to ES5) Compilation - specifically separate from transpiling ES6 and JSX to ES5, is the act of including assets, processing CSS files as JSON, or other mechanisms that can load and inject external assets and code into a file. In addition, there are all sorts of build steps that can analyze your code and even optimize it for you. Minification and Compression - typically part of - but not exclusively controlled by - compilation, is the act of minifying and compressing a JS file into fewer and/or smaller files Source-Mapping - another optional part of compilation is building source maps, which help identify the line in the original source code that corresponds with the line in the output code (i.e. where an error occurred) For React, there are specific build tool plugins, such as the babel-react-optimize presets that involves compiling code into a format that optimizes React, such as automatically compiling any React.createElement() calls into a JavaScript Object that inlines right into the source code: class MyComponent extends React.Component { render() { return ( <div className={this.props.className}> <span>Hello World</span> </div> ); } } becomes class MyComponent extends React.Component { render() { return ( _jsx('div', { className: this.props.className }, void 0, _jsx('span', {}, void 0, 'Hello World') ) ); } } See more about how compilers can help optimize React.

Compare and contrast the various React Component lifecycle methods. How might understanding these help build certain interfaces/features?

There are several React lifecycle methods that help us manage the asynchronous and non-determinate nature of a Component during it's lifetime in an app - we need provided methods to help us handle when a component is created, rendered, updates, or removed from the DOM. Let's first classify and define the life-cycle methods: The "Will's" - invoked right before the event represented occurs. componentWillMount() - Invoked once, both on the client and server, immediately before the initial rendering occurs. If you call setState within this method, render() will see the updated state and will be executed only once despite the state change. componentWillReceiveProps(object nextProps) - Invoked when a component is receiving new props. This method is not called for the initial render. Calling this.setState() within this function will not trigger an additional render. One common mistake is for code executed during this lifecycle method to assume that props have changed. componentWillUnmount() - Invoked immediately before a component is unmounted from the DOM. Perform any necessary cleanup in this method, such as invalidating timers or cleaning up any DOM elements that were created in componentDidMount. componentWillUpdate(object nextProps, object nextState) - Invoked immediately before rendering when new props or state are being received. This method is not called for the initial render. The "Did's" componentDidMount() - Invoked once, only on the client (not on the server), immediately after the initial rendering occurs. At this point in the lifecycle, you can access any refs to your children (e.g., to access the underlying DOM representation). The componentDidMount() method of child components is invoked before that of the parent component. componentDidUpdate(object prevProps, object prevState) - Invoked immediately after the component's updates are flushed to the DOM. This method is not called for the initial render. Use this as an opportunity to operate on the DOM when the component has been updated. The "Should's" shouldComponentUpdate(object nextState, object nextProps) - Invoked before rendering when new props or state are being received. This method is not called for the initial render or when forceUpdate() is used. Use this as an opportunity to return false when you're certain that the transition to the new props and state will not require a component update. Having a strong understanding of how these fit together - and how setState() or forceUpdate() affect the lifecycle - will help the conscious React developer build robust UIs.

What are pure functional Components?

Traditional React Components as we have seen thus far are creating a class with class Example extends React.Component or React.createClass(). These create stateful components if we ever set the state (i.e. this.setState(), getInitialState(), or this.state = {} inside a constructor()). If we have no intention for a Component to need state, or to need lifecycle methods, we can actually write Components with a pure function, hence the term "pure functional Component": function Date(props){ let {msg="The date is:"} = props let now = new Date() return <div> <span>{msg}</span> <time>{now.toLocaleDateString()}</time> </div> } This function that returns a React Element can be used whereever we see fit: DOM.render(<div><Date msg="Today is"/><div>) You might notice that <Date/> also takes a prop - we can still pass information into the Component.

How might React handle or restrict Props to certain types, or require certain Props to exist?

You may recall a previous example that looked like the following (some parts of the code left out): class LazyLoad extends Component { constructor(p){ super(p) this.state = { loaded:0 } } render(){ let {children} = this.props, {loaded} = this.state return <div> {loaded && children} </div> } } When rendering the <LazyLoad/>, we can pass in props (i.e. <LazyLoad top={0}/>). Props are essentially inputs or values being passed down to one Component from the parent rendering context, and the code that passes the props to the element may not be compliant with your code. For example, top here seems to be just a number, but would I be able to verify that the prop is in-fact a number before my component is rendered? It's certainly possible to write this code in each and every Component that uses props. However, React provides us a much simpler and shorter solution: Prop Types. let p = React.PropTypes LazyLoad.PropTypes = { top: p.number } When using React's non-minified development version (i.e. when building and testing in development), React will throw an error to alert you of any instances where a Prop is either missing or the wrong type. Above, top should always be a number. We can make top a required prop by adding: let p = React.PropTypes LazyLoad.PropTypes = { top: p.number.isRequired } PropTypes can be used to test Props for any kind of value (see https://facebook.github.io/react/docs/reusable-components.html for more info). Here's a few quick type-checkers React has for JavaScript's built-in types: React.PropTypes.array, React.PropTypes.bool, React.PropTypes.func, React.PropTypes.number, React.PropTypes.object, React.PropTypes.string, React.PropTypes.symbol, We can also test that props are React and DOM types: React.PropTypes.node, React.PropTypes.element, And we have the ability to test more complex types, such as "shapes", "instances of", or "collections of": React.PropTypes.instanceOf(Message), React.PropTypes.oneOf(['News', 'Photos']), React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.number, React.PropTypes.instanceOf(Message)]) React.PropTypes.arrayOf(React.PropTypes.number), React.PropTypes.shape({ color: React.PropTypes.string, fontSize: React.PropTypes.number }) Use these PropTypes to produce errors and track down bugs. When used effectively, PropTypes will prevent your team from losing too much time in the debugging and documentation process, ensuring stricter standards and understanding of your growing library of Components.

Which feature can we use to cause a component to render only when its ID changes?

shouldComponentUpdate


Kaugnay na mga set ng pag-aaral

Study For Patho # 2 Several, Chapter 28 Alterations of Pulmonary functions in children mid, Pathophysiology Ch. 27 Alterations of Pulmonary Function mid, Chapter 26 Structure and function of pulmonary system, Chapter 26: Structure/function of the Pul...

View Set

Ch 11 Childhood and Neurodevelopmental Disorders

View Set

Props, Set Dressing, and Scenery

View Set

Acctg 1 SMC Hicks Midterm Chapter 1-5

View Set

Personal Finance Car Buying Questions

View Set