TECHY360
Everything You Need To Know About Tech

9 useful tips for every React.js Developers

0 12

I have been using  React.js  for 6 months now. Yes, it sounds like a short time, but for the ever-changing world of JS frameworks, it is very long! I already gave advice to newcomers, and therefore decided that it would be a good idea to bring them together. 

I expect you to understand the basic concepts; If the words “component”, “prop” and “state” are not familiar to you, then you should first familiarize yourself with the official page  Getting started and  Tutorial. In addition, I will use  JSX, since it is a more convenient syntax for writing components.

1. This is just a library for working with views.

First, we analyze the basics. React is not just another MVC framework or some other framework. This is just a library for rendering your submissions. If you come from the world of MVC, then you should understand that React is only “V”, and “M” and “C” will have to search somewhere else.

2. Components should be small.

Sounds obvious, doesn’t it? Every intelligent developer knows that small classes/modules/anything is easier to understand and maintain. My mistake at the beginning of working with React was that I did not understand how small my components should be. Of course, the final size will depend on many factors, but in general, it is worth making the components much smaller than what you initially plan. As an example, I will give a component that displays the latest entry in my blog on the main page of the site:

const LatestPostsComponent = props => (
  <section>
    <div><h1>Latest posts</h1></div>
    <div>
      { props.posts.map(post => <PostPreview key={post.slug} post={post}/>) }
    </div>
  </section>
);

The component itself is  <section>, with two  <div>‘s inside. The first is the title, and the second displays some data, displaying  <PostPreview> each element. Something like this should be your components.

3. Write functional components

There used to be only two ways to define React components, the first is  React.createClass():

const MyComponent = React.createClass({
  render: function() {
    return <div className={this.props.className}/>;
  }
});

… and the second – ES6 classes:

class MyComponent extends React.Component {
  render() {
    return <div className={this.props.className}/>;
  }
}

React 0.14 brought a  new syntax for defining components as functions of properties:

const MyComponent = props => (
  <div className={props.className}/>
);

This is my favorite way. In addition to more understandable syntax, this approach makes it clear when the component should be divided. Consider the previous example and imagine that we have not yet divided it:

class LatestPostsComponent extends React.Component {
  render() {
    const postPreviews = renderPostPreviews();

    return (
      <section>
        <div><h1>Latest posts</h1></div>
        <div>
          { postPreviews }
        </div>
      </section>
    );
  }

  renderPostPreviews() {
    return this.props.posts.map(post => this.renderPostPreview(post));
  }

  renderPostPreview(post) {
    return (
      <article>
        <h3><a href={`/post/${post.slug}`}>{post.title}</a></h3>
        <time pubdate><em>{post.posted}</em></time>
        <div>
          <span>{post.blurb}</span>
          <a href={`/post/${post.slug}`}>Read more...</a>
        </div>
      </article>
    );
  }
}

This class is not so bad. We have already taken a couple of methods from the draw method and pretty well encapsulated the very idea of ​​rendering the last posts. Rewrite this code using the functional syntax:

const LatestPostsComponent = props => {
  const postPreviews = renderPostPreviews(props.posts);

  return (
    <section>
      <div><h1>Latest posts</h1></div>
      <div>
        { postPreviews }
      </div>
    </section>
  );
};

const renderPostPreviews = posts => (
  posts.map(post => this.renderPostPreview(post))
);

const renderPostPreview = post => (
  <article>
    <h3><a href={`/post/${post.slug}`}>{post.title}</a></h3>
    <time pubdate><em>{post.posted}</em></time>
    <div>
      <span>{post.blurb}</span>
      <a href={`/post/${post.slug}`}>Read more...</a>
    </div>
  </article>
);

The code has hardly changed, however, now we have pure functions, not class methods. However, these are very different things. In the first example, I see  class LatestPostsComponent { and immediately look at the code in search of a closing bracket, thinking: “This is the end of the class, and therefore the end of the component.” For comparison, in the second example, I see  const LatestPostsComponent = props => { and look only for the end of this function, thinking: “This is the end of the function and therefore the end of the module. Stop, but what is the code after my component, in the same module? And, this is another function that accepts data and draws a presentation! I can take it to a separate component! ”

In the future, React will be optimized so that the functional components are more efficient, but so far their performance is a big question; I recommend that you review this material to clarify the picture.

It is important to note that the functional components have several limitations, which I consider their strengths. The first is that you cannot bind to a functional component ref. Although  ref it is a convenient way for a component to communicate with its descendants, I believe that this is not for functional React, but rather for imperative jQuery.

The second limitation is that a state cannot be attached to functional components, and this is also an advantage since I advise: …

4. Write stateless components

It is worth saying that I felt the most pain when writing React applications from components with extensive use of the state.

States make testing difficult

The easiest way is to test pure functions, so why spoil them by adding states? After adding states, we need to bring all the components to the desired state, and also to sort through all combinations of states and properties, which is very inconvenient.

States make it difficult to understand components

Reading the code of a state-saturated component, many questions arise: “Has this state already been initialized?”, “What happens if I change this state here?”, “Where does this state change?”, “Is there a race condition ( Race condition)? ”and the like – and this is an extra headache.

States make it too easy to add business logic to components

We should not engage in determining the behavior of a component. Remember, React is a library for working with views, and therefore, when a component has access to the state of an application, you will have to restrain from the temptation to add calculations or validation to it, because there’s no place for them. In addition, mixing the rendering logic and business logic will further complicate testing.

States make it difficult to exchange information between parts of an application.

When a component has a state, it can be passed down the component hierarchy, but not in other directions.

Of course, sometimes the fact that a particular component has full access to a particular state makes sense, and then you can use it  this.setState — this is a completely legitimate part of the React-components API. For example, if a user enters information in a field, it makes no sense to make each keystroke available to the entire application, therefore, it will suffice to track the state of the field to the field itself, and at the end of the input, pass on the entered value.

In short, be extremely careful when adding states. When you start, it will be very difficult to refrain from adding another “little feature”.

5. Use Redux.js

In the first paragraph, I said that React is only needed for working with views. But where to put all the states and logic?

You’ve probably heard about  Flux  – SPA (style/pattern/architecture) for developing web applications that are often used with React. There are several frameworks that implement the ideas of Flux, but I definitely recommend Redux.js.

Here is a brief description of how Redux works:

Related Posts
1 of 7
  1. Components  accept callbacks as properties and trigger them when a UI event occurs;
  2. These callbacks  create and send actions  depending on the event;
  3. Reducers  handle  actions by calculating a new  state ;
  4. The new  state of the  entire application is placed in  one repository ;
  5. Components get a new state as property and redraw themselves if necessary.

Most of the above features are not only found in Redux, but it provides very simple implementation and a  tiny API. Having transferred a rather big project from Alt.js to Redux, I highlighted several advantages:

  • Redyusery – a pure function that simply does the following:  oldState + action = newState. Each reducer calculates a separate part of the state, which is then combined. This greatly simplifies the testing of business logic and states.
  • The API is smaller, simpler and better documented.
  • If you use Redux as it should be, a very small number of components will rely on it; the rest will only receive states and callbacks as properties.

There are several other libraries that perfectly complement Redux:

  • Immutable.js – immutable data structures in javascript! They should be stored state so that they do not accidentally change.
  • redux-thunk – it is used when, in addition to a change in state, some other “side effect” should arise.
  • reselect – use it to create composable views.

6. Always use propTypes

propTypes provide us with a very simple way to increase the security of our components. They look like this:

const ListOfNumbers = props => (
  <ol className={props.className}>
    {
      props.numbers.map(number => (
        <li>{number}</li>)
      )
    }
  </ol>
);

ListOfNumbers.propTypes = {
  className: React.PropTypes.string.isRequired,
  numbers: React.PropTypes.arrayOf(React.PropTypes.number)
};

During the development process, if a necessary property or a property of a different type is not passed to a component, React will create an error log. Benefits:

  • You can catch bugs in advance;
  • If you use  isRequired, you do not need to check for  undefined or  null;
  • Solving the issue of documentation.

Although it looks like a campaign for static typing, it is not. I prefer dynamic types for their simplicity, but propTypes further enhance the security of components, so I see no reason not to use them.

It is also worth setting up tests for failure when encountering propType errors. It is simple and works:

beforeAll(() => {
  console.error = error => {
    throw new Error(error);
  };
});

7. Use shallow rendering.

Testing React components can be tricky, since this topic is still evolving, and there is definitely no better approach. At the moment I prefer to use shallow rendering and shading rendering (prop assertions).

Shallow rendering is convenient in that it allows you to completely display one component without affecting any descendants. Instead, the returned object will report the types and properties of the children.

Most often, I write three types of unit tests for components:

Draw logic

Imagine a component that should conditionally draw an image or download icon:

const Image = props => {
  if (props.loading) {
    return <LoadingIcon/>;
  }

  return <img  data-src={props.src}/>;
};

We can test it like this:

describe('Image', () => {
  it('renders a loading icon when the image is loading', () => {
    const image = shallowRender(<Image loading={true}/>);

    expect(image.type).toEqual(LoadingIcon);
  });

  it('renders the image once it has loaded', () => {
    const image = shallowRender(<Image loading={false} src="https://example.com/image.jpg"/>);

    expect(image.type).toEqual('img');
  });
});

Elementary! It is worth noting that the API for shallow rendering is more complicated than in my example. The function is  shallowRender written by us as an API wrapper to simplify it.

Returning to the component  ListOfNumbers, here’s how to test its correctness:

describe('ListOfNumbers', () => {
  it('renders an item for each provided number', () => {
    const listOfNumbers = shallowRender(<ListOfNumbers className="red" numbers={[3, 4, 5, 6]}/>);

    expect(listOfNumbers.props.children.length).toEqual(4);
  });
});

Property conversion

In the last example, we climbed into the descendants of the component we were using to check if we were correctly drawn. This can be expanded if we confirm that the descendants are not only correct, but also obtained the necessary properties. This is especially useful when a component somehow converts properties before passing them. For example, consider this component, which takes the names of CSS classes as an array of strings and passes them as one string:

const TextWithArrayOfClassNames = props => (
  <div>
    <p className={props.classNames.join(' ')}>
     {props.text}
    </p>
  </div>
);

describe('TextWithArrayOfClassNames', () => {
  it('turns the array into a space-separated string', () => {
    const text = 'Hello, world!';
    const classNames = ['red', 'bold', 'float-right'];
    const textWithArrayOfClassNames = shallowRender(<TextWithArrayOfClassNames text={text} classNames={classNames}/>);

    const childClassNames = textWithArrayOfClassNames.props.children.props.className;
    expect(childClassNames).toEqual('red bold float-right');
  });
});

Often this approach is criticized for  props.children.props.children… Although such code is not very beautiful, it is useful in that it makes it clear that if there is too much in one test  props.children, the component should be reduced.

Another observation is that such tests are very dependent on the external implementation of the component in the sense that even the slightest change in the structure of the DOM breaks all the tests. I agree with this, but you can fight it, yes-yes, by reducing the size of the components, which will reduce the number of potentially unstable tests.

User interaction

Of course, the components are also interactive:

const RedInput = props => (
  <input className="red" onChange={props.onChange} />
)

Here is my favorite way to test:

describe('RedInput', () => {
  it('passes the event to the given callback when the value changes', () => {
    const callback = jasmine.createSpy();
    const redInput = shallowRender(<RedInput onChange={callback}/>);

    redInput.props.onChange('an event!');
    expect(callback).toHaveBeenCalledWith('an event!');
  });
});

The example is trivial, but I think you get the gist.

Integration testing

So far, we have considered only unit testing, but we need some higher level tests to test the entire application. I will not go into details:

  1. Draw the entire component tree (instead of shallow rendering).
  2. Delve into the DOM (using  React TestUtils,  jQuery, etc.) to find the most important elements, and:
    • confirm their HTML attributes or content, or
    • simulate DOM events and confirm side effects (changes in DOM or paths, AJAX calls, etc.).

8. Use JSX, ES6, Babel, Webpack and NPM

The only thing specific to react is JSX. Its only drawback is a slight increase in build time, but this is solved with the help of  Babel.

If we started using Babel, there is no reason to stop using all the features of ES6. One feels that JavaScript is starting to grow as a language, considering how long it takes to prepare all the tools.

We end up using  Webpack to bundle our code and  NPM  to manage packages.

9. Use the React and Redux developer tools.

Speaking of tools, React and Redux are very good in this regard. Tools for React allow you to check the drawn tree of React elements, which is very convenient. Redux tools are even more impressive. You can add them as a  browser dependency or extension.

Get real time updates directly on you device, subscribe now.

Comments
Loading...

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More