你的位置:首页 > 操作系统

[操作系统]React.js Best Practices for 2016


2015 was the year of React with tons of new releases and developer conferences dedicated to the topic all over the world. For a detailed list of the most important milestones of last year, check out our React in 2015 wrap up.

 

The most interesting question for 2016: How should we write an application and what are the recommended libraries?

 

As a developer working for a long time with React.js I have my own answers and best practices, but it's possible that you won’t agree on everything with me. I’m interested in your ideas and opinions: please leave a comment so we can discuss them.

 

React.js logo - Best Practices for 2016

 

If you are just getting started with React.js, check out our React.js tutorial, or the React howto by Pete Hunt.

 

Dealing with data

 

Handling data in a React.js application is super easy, but challenging at the same time. 

It happens because you can pass properties to a React component in a lot of ways to build a rendering tree from it; however it's not always obvious how you should update your view.

 

2015 started with the releases of different Flux libraries and continued with more functional and reactive solutions.

 

Let's see where we are now:

 

Flux

 

According to our experience, Flux is often overused (meaning that people use it even if they don't even need it).

 

Flux provides a clean way to store and update your application's state and trigger rendering when it's needed.

 

Flux can be useful for the app's global states like: managing logged in user, the state of a router or active account but it can turn quickly into pain if you start to manage your temporary or local data with it.

 

We don’t recommend using Flux for managing route-related data like /items/:itemId. Instead, just fetch it and store it in your component's state. In this case, it will be destroyed when your component goes away.

 

If you need more info about Flux, The Evolution of Flux Frameworks is a great read.

 

Use redux

 

Redux is a predictable state container for JavaScript apps.

 

If you think you need Flux or a similar solution you should check out redux and Dan Abramov's Getting started with redux course to quickly boost your development skills.

 

Redux evolves the ideas of Flux but avoids its complexity by taking cues from Elm.

 

Keep your state flat

 

API's often return nested resources. It can be hard to deal with them in a Flux or Redux-based architecture. We recommend to flatten them with a library like normalizr and keep your state as flat as possible.

 

Hint for pros:

 

const data = normalize(response, arrayOf(schema.user))

 

state = _.merge(state, data.entities)  

(we use isomorphic-fetch to communicate with our APIs)

 

Use immutable states

 

Shared mutable state is the root of all evil - Pete Hunt, React.js Conf 2015

 

Immutable logo for React.js Best Practices 2016

 

Immutable object is an object whose state cannot be modified after it is created.

 

Immutable objects can save us all a headache and improve the rendering performance with their reference-level equality checks. Like in the shouldComponentUpdate:

 

shouldComponentUpdate(nexProps) {  

 // instead of object deep comparsion

 return this.props.immutableFoo !== nexProps.immutableFoo

}

How to achieve immutability in JavaScript? 

The hard way is to be careful and write code like the example below, which you should always check in your unit tests with deep-freeze-node (freeze before the mutation and verify the result after it).

 

return {  

  ...state,

  foo

}

 

return arr1.concat(arr2)  

Believe me, these were the pretty obvious examples.

 

The less complicated way but also less natural one is to use Immutable.js.

 

import { fromJS } from 'immutable'

 

const state = fromJS({ bar: 'biz' })  

const newState = foo.set('bar', 'baz')  

Immutable.js is fast, and the idea behind it is beautiful. I recommend watching the Immutable Data and React video by Lee Byron even if you don't want to use it. It will give deep insight to understand how it works.

 

Observables and reactive solutions

 

If you don't like Flux/Redux or just want to be more reactive, don't be disappointed! There are other solutions to deal with your data. Here is a short list of libraries what you are probably looking for:

 

cycle.js ("A functional and reactive JavaScript framework for cleaner code")

rx-flux ("The Flux architecture with RxJS")

redux-rx ("RxJS utilities for Redux.")

mobservable ("Observable data. Reactive functions. Simple code.")

Routing

 

Almost every client side application has some routing. If you are using React.js in a browser, you will reach the point when you should pick a library.

 

Our chosen one is the react-router by the excellent rackt community. Rackt always ships quality resources for React.js lovers.

 

To integrate react-router check out their documentation, but what's more important here: if you use Flux/Redux we recommend to keep your router's state in sync with your store/global state.

 

Synchronized router states will help you to control router behaviors by Flux/Redux actions and read router states and parameters in your components.

 

Redux users can simply do it with the redux-simple-router library.

 

Code splitting, lazy loading

 

Only a few of webpack users know that it's possible to split your application’s code to separate the bundler's output to multiple JavaScript chunks:

 

require.ensure([], () => {  

  const Profile = require('./Profile.js')

  this.setState({

    currentComponent: Profile

  })

})

It can be extremely useful in large applications because the user's browser doesn't have to download rarely used codes like the profile page after every deploy.

 

Having more chunks will cause more HTTP requests - but that’s not a problem with HTTP/2 multiplexed.

 

Combining with chunk hashing you can also optimize your cache hit ratio after code changes.

 

The next version of react-router will help a lot in code splitting.

 

For the future of react-router check out this blog post by Ryan Florence: Welcome to Future of Web Application Delivery.

 

Components

 

A lot of people are complaining about JSX. First of all, you should know that it’s optional in React.

 

At the end of the day, it will be compiled to JavaScript with Babel. You can write JavaScript instead of JSX, but it feels more natural to use JSX while you are working with HTML. 

Especially because even less technical people could still understand and modify the required parts.

 

JSX is a JavaScript syntax extension that looks similar to

 

If you want to read more about JSX check out the JSX Looks Like An Abomination - But it’s Good for You article.

 

Use Classes

 

React works well with ES2015 classes.

 

class HelloMessage extends React.Component {  

  render() {

    return <div>Hello {this.props.name}</div>

  }

}

We prefer higher order components over mixins so for us leaving createClass was more like a syntactical question rather than a technical one. We believe there is nothing wrong with using createClass over React.Component and vice-versa.

 

PropType

 

If you still don't check your properties, you should start 2016 with fixing this. It can save hours for you, believe me.

 

MyComponent.propTypes = {  

  isLoading: PropTypes.bool.isRequired,

  items: ImmutablePropTypes.listOf(

    ImmutablePropTypes.contains({

      name: PropTypes.string.isRequired,

    })

  ).isRequired

}

Yes, it's possible to validate Immutable.js properties as well with react-immutable-proptypes.

 

Higher order components

 

Now that mixins are dead and not supported in ES6 Class components we should look for a different approach.

 

What is a higher order component?

 

PassData({ foo: 'bar' })(MyComponent)  

Basically, you compose a new component from your original one and extend its behaviour. You can use it in various situations like authentication: requireAuth({ role: 'admin' })(MyComponent) (check for a user in higher component and redirect if the user is not logged in) or connecting your component with Flux/Redux store.

 

At RisingStack, we also like to separate data fetching and controller-like logic to higher order components and keep our views as simple as possible.

 

Testing

 

Testing with good test coverage must be an important part of your development cycle. Luckily, the React.js community came up with excellent libraries to help us achieve this.

 

Component testing

 

One of our favorite library for component testing is enzyme by AirBnb. With it's shallow rendering feature you can test logic and rendering output of your components, which is pretty amazing. It still cannot replace your selenium tests, but you can step up to a new level of frontend testing with it.

 

it('simulates click events', () => {  

  const onButtonClick = sinon.spy()

  const wrapper = shallow(

    <Foo onButtonClick={onButtonClick} />

  )

  wrapper.find('button').simulate('click')

  expect(onButtonClick.calledOnce).to.be.true

})

Looks neat, isn't it?

 

Do you use chai as assertion library? You will like chai-enyzime!

 

Redux testing

 

Testing a reducer should be easy, it responds to the incoming actions and turns the previous state to a new one:

 

it('should set token', () => {  

  const nextState = reducer(undefined, {

    type: USER_SET_TOKEN,

    token: 'my-token'

  })

 

  // immutable.js state output

  expect(nextState.toJS()).to.be.eql({

    token: 'my-token'

  })

})

Testing actions is simple until you start to use async ones. For testing async redux actions we recommend to check out redux-mock-store, it can help a lot.

 

it('should dispatch action', (done) => {  

  const getState = {}

  const action = { type: 'ADD_TODO' }

  const expectedActions = [action]

 

  const store = mockStore(getState, expectedActions, done)

  store.dispatch(action)

})

For deeper redux testing visit the official documentation.

 

使用native react开发稳赚理财助手app ing