The philosophy of React
React and me, sitting in a tree..
If you read my previous post about testing React, you’ll know that I think React is really rather cool. It provides a great structure for creating well-organised front-end code (only front-end: it isn’t an mvc framework), and facilitates the kind of whizzy asynchrony we are coming to expect from modern websites.
Although it’s not too hard to dive into a simple project with the plethora of quick start tutorials out there, delving deeper into anything with complex requirements demands a confident grasp of React’s core principles. This is exactly the challenge my team faced when we decided to use React for our Makers Academy final project — a webpage builder tool that can write CSS and HTML code automatically. Check out our project here.
This blog post is not a code-along guide; there are plenty of those out there, like this one. It is designed to help those just starting to dabble in React to get a better grasp of the basic concepts involved — i.e. the stuff I wish I was confident with from the start!
It’s all about components..
(Mad) props and state
When tackling React for the first time, some of the trickiest things to get used to are how different components interact, the flow of communication and control and how component re-rendering is triggered. A lot of which comes down to an understanding of props and state.
Props: Any data that is passed to a component as part of it’s creation is classed as props. The data flows from the top downwards, ie. from parent to child. If a parent passes new props to an existing child this will trigger the child to re-render. The props are saved for reference throughout the life of the component but the component itself does not have the power to change them. Think of it like a helpful email from your Mum to remind you it’s Aunt Betty’s birthday. You can use and refer to that information any time you like, but you can’t re-write the message.
State: Any data that a component decides to store within itself is it’s state. Like a teenager’s bedroom, state is the sanctuary over which a component can retain complete control. Every time a component’s setState() function is called, it performs the dual actions of both updating the state, and re-rendering the component. Programming in a React mindset means trying to minimise the use of state — it should only contain data that needs to respond to change, and ideally components shouldn’t know too much about other components.
Flow of control
React really forces you to think carefully about the structure of your app, as both data (in the form of props and state) and control flows in a very linear way between parents and children. Parents have the power to create their children, but also can exert control by passing them new props. Children can also control their parents. In the example below, a function with the ability to set the state of a parent component is tethered to that parent component using the “bind” function. This means the function can travel about in your programme, and it will always remember it’s own sense of “this” — ie. which component it should be modifying. This allows the parent to pass the function down to it’s child (or even grandchild) via props, hence giving the child the power to trigger the function and impact their parent. So clicking the “Red” button in the Child component below changes the state of the Parent!
Unlike real-life parents, React does not really approve of siblings (or more distant relations) communicating with each other. This is one of the most challenging restrictions in using React, and why there are a raft of React add-on libraries designed specifically to address this issue (Flux, Redux, Reflux etc).
This posed a problem for my team in the early stages of our project. Our first idea was to have a “control panel” component that would allow a user to change the appearance of the page viewer component. As these two components would have been siblings (both children of our master App component), communication between them would have routed through App. This would have been a rather un-React architecture, as it would involve App becoming a very stateful data store, knowing too much about it’s children.
Then we discovered the React Popup add-on, which allowed us to render a popup menu of options as a child of any component that was clicked within the viewer. So not only did this create a nicer user experience for our app, but it streamlined the architecture beautifully!
To construct, or not to construct, that is the question…
Each React component must have a render function, but they will often also have a custom constructor, as per the Parent class in the example above. The constructor function is optional, and should only be used in the scenarios below:
- If you need to bind functions to the component
- If you want to set initial values for a component’s state
Note that the constructor function only runs when a component is first created, not when it is re-rendered. Therefore, if you set a component’s state using props in the constructor, this won’t then automatically update if new props are passed in.
Is it a bird? Is it a plane? No, it’s SUPERPROPS!
Every constructor function must always start with either super() or super(props). Use the former if you do not need to refer to props anywhere in your constructor. Otherwise, you’ll need to pass in props as an argument to the function and call super(props) inside it.
And I’m still learning..
Please do comment with your thoughts, feedback and top React tips!