Read-only Nature of React Props
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
- 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
- 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.