Read-only Nature of React Props

· 3 min read

When developing React components, Props cannot be modified. But why? How to ensure they aren’t modified in actual development as much as possible? Let’s continue exploring.

Explanation of Props in React

Whether you declare a component as a function or a class, it must never modify its own props. React is pretty flexible but it has a single strict rule: All React components must act like pure functions with respect to their props.

Conclusion

  1. The React library doesn’t enforce that props cannot be modified, but the rule is that they shouldn’t be modified - yes, this is a Rule
  2. Neither function components nor class components should modify Props to avoid side effects

Example

// App renders a user.name and a profile
const App = (props) =>
  React.createElement("div", null, [
    props.user.name,
    React.createElement(Profile, props)
  ]);

// Profile changes the user.name and renders it
// Now App has the wrong DOM.
const Profile = ({ user }) => {
  user.name = "Voldemort"; // Uh oh!
  return React.createElement("div", null, user.name);
};

// Render the App and give it props
ReactDOM.render(
  React.createElement(App, { user: { name: "Hermione" } }),
  document.getElementById("app"));

As shown above, although we modified user.name in the child component, the parent component still prints Hermione. Why? Because props is an object, and the object itself wasn’t modified - React component updates use shallow comparison.

React Component TypeScript Definition

 class Component<P, S> {
        constructor(props: Readonly<P>);
        ...

As can be seen above, Props are read-only parameters, which ensures a certain level of safety. If we try to assign directly, TSC will report an error

So is that all good? No, what if the parameter is an object type?

As shown above, it won’t report an error, but it’s a BUG. The fundamental problem is that the current Type definition cannot ensure that nested properties of properties are read-only. Regarding this, the community has already discussed and proposed solutions, but the new definitions haven’t been merged in yet.

Discussion here

How can we ensure absolute immutability currently? You can manually add the new definitions to your project.

Select in Redux Saga

In components, we use Redux values as Props, so they should still be used read-only. Another usage scenario is in effects using const state = yield select(); - should this state also be read-only?

YES, select actually calls store.get(key) for a particular state. Note that it’s actually an object, so if we modify this object in effects, we’re actually modifying the single source of truth state tree. Therefore, when we get state through select, we must maintain read-only usage

Props in Angular and Vue?

Angular and Vue don’t explicitly state that parameters passed from parent components cannot be modified, but note that they are components in usage, so it’s recommended not to modify them either.

Final Thoughts

Understanding these fundamental design rules and concepts is beneficial for using these technologies and also helps with design patterns.

References