React Components: that can help you create amazing websites.
Controlled vs. Uncontrolled Components in React
In most applications, there is a need for input and some form of interaction with users, allowing them to type something, upload a file, select a field, and so on. React deals with user interaction it in two distinct ways – controlled and uncontrolled components.
The value of controlled components, as their name suggests, is controlled by React by providing a value to the element that interacts with the user, while uncontrolled elements do not get a value property. Thanks to that, we have a single source of truth which happens to be the React state, so there is no mismatch between what we are seeing on the screen and what we currently have in our state. The developer needs to pass a function that will respond to user interaction with a form, which will change its state.
class ControlledInput extends React.Component { state = { value: "" }; onChange = (e) => this.setState({ value: e.target.value }); render() { return ( <input value={this.state.value} onChange={this.onChange} /> ); } }
In uncontrolled React components, we do not care about how the value changes, but if we want to know the exact value, we simply access it through ref.
class UncontrolledInput extends React.Component { input = React.createRef(); getValue = () => { console.log(this.input.current.value); }; render() { return ( <input ref={this.input}/> ); } }
So which should be used when? I would say that controlled components are the way to go in most cases, but there are some exceptions. For example, one case where you need to use uncontrolled components in React is the file type input, as its value is read-only and cannot be programmatically set (user interaction is required). Also, I find controlled components easier to read and easier to work with. Doing validation for controlled components is based on the rerender, the state can be changed, and we can easily indicate that there is something wrong with the input (e.g., format or being empty).
Refs
We already mentioned refs, which are a special feature that was available in class components until hooks appeared in 16.8.
Refs can give the developer access to a React component or DOM element (depending on the type where we attach ref) through reference. It is considered a good practice to try to avoid them and use them only in must-have scenarios, as they make the code a bit harder to read and break the top-to-bottom data flow. Yet, there are cases where they are necessary, especially on DOM elements (e.g., changing focus programmatically). When attaching to a React component element, you can freely use methods from within that component that you are referring to. Still, this practice should be avoided as there are better ways to deal with it (e.g., lifting state up and moving functions to parent components).
Refs also have three different ways they can be accomplished:
- Using a string literal (legacy and should be avoided),
- Using a callback function that is being set in the ref attribute,
- By creating ref as React.createRef() and binding it to a class property and accessing it through it (be aware that references will be available from the componentDidMount lifecycle).
Finally, there are cases when refs aren’t passed down and times when you want to access a deeper reference element from the current component (e.g., you have a <Button> component that has an inside <input> DOM element and right now you are in a <Row> component, and from the row component you want to have access to input the DOM focus function. That’s where you would use forwardRef).
One case where reference is not being passed down is when there is a higher order component being used on a component—the reason is quite understandable as ref is NOT a prop (similar to key) so it isn’t being passed down so it will be referencing the HOC instead of the component being wrapped by it. In such a case, we can use React.forwardRef which takes props and refs as arguments, which can be then assigned to prop and passed down to the component that we want to access.
function withNewReference(Component) { class Hoc extends React.Component { render() { const {forwardedRef, ...props} = this.props; return <Component ref={forwardedRef} {...props}/>; } } return React.forwardRef((props, ref) => { return <Hoc {...props} forwardedRef={ref} />; }); }
Error Boundaries
The more complex things get, the higher the probability that something will go wrong. That’s why error boundaries are a part of React. So how do they work?
If something goes wrong and there’s no error boundary as its parent, it will result in the whole React app failing. It is better to not display information rather than mislead users and display wrong information, but it doesn’t necessarily mean that you should crash the whole application and show a white screen. With error boundaries, you have an added degree of flexibility that you can use. You can either use one across the whole app and display an error message, or use it in some widgets and simply not display them, or display a small amount of information in place of those widgets instead.
Remember that it is only about issues with declarative code rather than imperative code that you write for handling some events or calls. For these, you should still use the regular try/catch approach.
Error boundaries are also a place where you can send information to the Error Logger that you use (in the componentDidCatch lifecycle method).
class ErrorBoundary extends React.Component { state = { hasError: false }; static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, info) { logToErrorLogger(error, info); } render() { if (this.state.hasError) { return <div>Help, something went wrong.</div> ; } return this.props.children; } }
Higher Order Components
Higher Order Components (HOC) are often mentioned in React and are a very popular pattern, one that you will probably use (or already did so). If you’re familiar with HOCs, you’ve probably seen withNavigation, connect, withRouter in many libraries.
HOCs are just functions that take a component as an argument and will return a new component with extended capabilities compared to the one without the HOC wrapper. Thanks to that, you can achieve some easily extensible functions that could enhance your components (e.g., access to navigation). HOCs can also take a few forms called depending on what we have, the only argument always required being a component, but it can take extra arguments—some options, or like in connect, you first invoke a function with configurations that later returns a function which takes an argument component and returns HOC.
There are a few things that you could add and should avoid:
- Add a display name for your wrapper HOC function (so you know that it is, in fact, a HOC by changing your HOC component display name).
- Do not use HOC inside a render method—you should already be using an enhanced component inside of it, instead of creating a new HOC component there due remounting it all the time and losing its current state.
- Static methods are not copied over, so if you want to have some static methods inside of your newly created HOC, you need to copy them over yourself.
- The mentioned Refs are not passed, so use React.forwardRef as previously mentioned for solving such issues.
export function importantHoc() { return (Component) => class extends React.Component { importantFunction = () => { console.log("Very Important Function"); }; render() { return ( <Component {...this.props} importantFunction={this.importantFunction} /> ); } }; }
Styling
Styling is not necessarily related to React itself but it is worth mentioning for a number of reasons.
First of all, the regular CSS/inline styles apply here as normal and you can simply add class names from CSS in the className attribute, and it will work correctly. The inline styling is a bit different than the regular HTML styling. The string is not being passed down with styles but rather objects with correct values for each. Style attributes are also camelCased, so border-radius becomes borderRadius and so on.
React seems to have popularized a few solutions that became commonplace not only in React, such as CSS modules that were recently integrated within CRA, where you can simply import name.modules.css and use its classes like properties to style your component (some IDEs, e.g., WebStorm, also have Autocomplete for that, which tells you what names are available).
Another solution that is also popular in React is CSS-in-JS (e.g., the emotion library). Just to point out again, CSS modules and emotion (or CSS-in-JS in general) are not limited to React.
To develop your custom website using React JS, please visit our technology page.
Content Source:
- toptal.com
- reactjs.org