Skip to content

Latest commit

 

History

History
170 lines (112 loc) · 9.27 KB

File metadata and controls

170 lines (112 loc) · 9.27 KB

Props, States and Instance Variables

Debugging mutable state is a brainteaser: "The butcher lives in the red house on Wed. The odd houses were painted on Sunday..."


An instance of the Go's Board class has several attributes that describe what a game of Go looks like at a particular moment in time. This is a common paradigm in React: get familiar with the snapshot concept.


There is an internal joke in the team that React should have been called "Schedule" because React does not want to be fully "reactive".

Every serious applications need to represent states over time. Root of all evil is state changing over time.

Human mind is not good at keeping track of state changing over time.

The root of all evil in an interactive application is managing state

State flows down the components, and events flow up. While properties should never be changed, state is mutable.

Properties can't be changed because they are inherited every time the component is rendered, so any changes will be lost.

There's a deeper reason why it's so important to make data flow clear and simple: it encourages you to keep state in as few places as possible and make most of your components stateless.

Once you've grokked the basics, this is perhaps the most important thing to know about React. To think in React is to find the minimal amount of state necessary to represent your app, and calculate everything based on that. This is because state is unpredictable. Props are, for the most part, derived from other props and state, but state can be anything. The more state in your application, the harder it is to reason about it. As much as possible, state in React should be an implementation detail — a necessary evil, not a crutch.

Warning: The worst thing you can do is call setState right after a render() in componentDidMount() or componentDidUpdate(). It means most render() will be called twice!

You can have instance properties for state that do not control how a component renders like this._timer

Note: Data flow down the component hierarchy. Event typically flow upstream to the owner component so they can make the decision to re-render child components.

  • Data only flows from parents to the children.
  • Children thus only have the logic to display the data, not modify it.
  • Children also handle events and then inform parents via callback or events. Parents then modify their own state.

Child to Parent Communication

Here we are talking about component-component communication, not data-fetching problem which are solved by Flux.

If you're not using the Flux pattern (where the parent widget listens to Stores that are affected by Action Creators invoked by the child elements), the idiomatic way to do this is to pass callbacks that affect the overall widget through props - this can be a bit awkward when you are passing a callback down several levels.

Promises are great to use when a child component needs to wait on a parent component's asynchronous request.

// Here the owner of <span> is Foo but the parent is the <div>
class Foo {
  render() {
    return <div><span /></div>
  }
}

Sub-tree and Re-rendering

The performance cost model of React is very simple to understand: every setState re-renders the whole sub-tree. If you want to squeeze out performance, call setState as low as possible and use shouldComponentUpdate to prevent re-rendering an large sub-tree.

Single Source of Truth

Parent is always the source of truth for all its children. Data pass down, never up.

Props (Immutable)

Properties are a mechanism for the outside world (users of the component) to configure your component. State is your internal data maintenance. So if you consider an analogy with object-oriented programming this.props is like all the arguments passed to a class constructor, while this.state is a bag of your private properties.

Props are a way to configure a component.

Object.isFrozen(this.props) === true

A component can change its state but its props are immutable, which is good feature because there should ideally be a single source of truth.

Don't feed any data as props to your ROOT component (if you can help it) Have your root component manage state.

A very hard to maintain mistake is to have the root/top component passing props to deeply nested children. You may end up with a lot of intermediate components that pass a lot of props to their children without even using it.

State

Think of React's state as data changes over time. Props are immutable whilst states can change over time and is bad enough.

Local-mutable data is stateful.

State == value of an identity at a time

State for React UI can exists in many forms:

  • Server RESTful returned values
  • URL route change
  • UI state, like toggle state, pagination, tab panel, spinner, drawer hide/show
  • Parent app state (props)

Warning: Do not sync states, you will screw it up and make it out of sync! Because you need a single source of truth.

Do not use state, use props unless you absolutely know what you are doing!

States are for things that are temporary and don't need to be persisted. Like whether the modal isVisible, whether the editor isEditing. Modal component won't worry if that state is lost for example.

Don't put business logic in your state. While UI/render logic in component is OK, business logic is a massive code smell.

But when so much for your application's state is right there in the component, easily accessible by this.state, it can become really tempting to start putting things like calculations or validation into the component, where it does not belong.

This makes testing that much harder - you can't test render logic without the business logic getting in the way, and vice versa!

Instance Variables

Good for non-renderable data:

  • Removing event listeners
  • Callback cancellations
  • Caches (often things from render)
  • Timer IDs
componentWillMount() {
  this.listenerId = AppStore.addListener((state) => {
    this.setState({user: state.user})
  });
}

componentWillUnmount() {
  AppStore.removeListener(this.listenerId);
}

PropTypes

model: React.PropTypes.instanceOf(Backbone.Model).isRequired
children: React.PropTypes.node

PropTypes.arrayOf(PropTypes.string)
PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.any))

PropTypes.arrayOf(PropTypes.shape({
  id: PropTypes.string.isRequired,
  label: PropTypes.string.isRequired,
  type: PropTypes.string
}))

Undo / Redo