bigdogs

More Articles

Building components

Rodrigo Perrote
Rodrigo PerroteJanuary 25, 2024 · min read

I was reading the latest article by Dan Abramov The two Reacts, where he talks about the two types of React. I invite you to read it because it is very interesting, and something caught my attention.

Dan uses a mathematical resource to explain how he sees user interfaces:

UI = F(state)

And it's not about the UI code being a single function that takes the state as an argument. Instead, it's more like reading that the UI is a function of the state. In other words, the state determines the user interface, and the user interface must be reinterpreted when the state changes.

This approach is crucial in how components are built because even in the decision not to use a state, it holds true. When we're in that design process that we carry out in our minds, it's essential to keep that idea in mind. But, how can we use that idea to our advantage?

I share a part of my process to understand what type of component I want to build, focusing on the question: Which part of the UI do I want to update?

Thinking in states

States are the minimal set of information that my application needs to remember, and any change in them will trigger the process of reinterpreting what we are drawing. To determine which states I'll need to have, being able to identify the component's responsibility is a crucial factor.

Business components

One of the first things I like to consider is what our component needs to address and whether it is directly associated with the activity it's developing or if it's something more common to web projects as a whole.

Let's take, for example, a members' management portal for clubs, and we have to create a component that displays all the members of a club in a list. The first step is to identify the responsibilities:

  1. Understand how to obtain information about the members - business logic

  2. Render a table - presentation logic

With this separation, we can now decide whether to keep the responsibilities in the same component or not. In our example, we know that we will have other tables in our application, so it would make sense to want to separate it.

function AssociateTable(){
    // Responsible for knowing where to obtain the information
    // We could also process the information if needed.
    return <Table />
}

function Table({data, columns}){
    return (
        <table>
            <tr>
                {columns.map((column)=><td>{column}</td>
              </tr>
              {data.map(item => <tr>
                <td>{item.name}</td>
                <td>{item.email}</td>
              </tr>)}
        </table>
    )

}

Not only asking ourselves if it's a business component can guide us, but also the context in which we are building our component: Does that state need to be in my component? Can we let the parent handle the state? Should the state be shared among components? Can we tie the component to its implementer? Or should it be independent?

All these questions will define where we want to place the states and whether it's something only needed to change the UI or if we will be handling application data.

Be cautious about dual state. Having a state in the parent that serves as the default value for our internal state can lead us to subscribe to changes.

function AssociateTable(){
    const [state, setState] = useState([])
    return <Table data={state} />
}

function Table({data}){
    const [state, setState] = useState(data)
    useEffect(() => {
        setState(data)
    }, [data])
    ...
}

Visual vs Logical

Let's not assume that a presentation component won't have logic. Suppose we want to be able to change the order of members by clicking on the columns; that involves a logical aspect where we need to reorder the incoming information (assuming we are doing this sorting on the front end and not on the back end).

Wanting to handle all the responsibilities of the table, we could end up writing a giant Table component where we have the part that processes the incoming data, the one listening for the click event on a column, and the one ultimately drawing the UI.

Or we could divide it!

function Table({data, columns}){
    const [sort, setSort] = useState({field: "name", order: "desc"})
    
    const sortBy = () => {
        ...
    }
    
    const handleChangeSort = (column) => setSort(prevState => {
        if(prevState === column){
            return {...prevState, order: "asc"}
        }
        return {...prevState, name: column}
    })
    
    const sortedData = data.sort(sortBy)
    
    return (
        <TableUI
            data={sortedData}
            columns={columns}
            onChangeSort={handleChangeSort}
        />
    )
}

function TableUI({data, columns, onChangeSort}){
    return (
        <table>
            <tr>
                {columns.map((column)=> (
                    <td onClick={() => onChangeSort(column)}>
                        {column}
                    </td>
                )}
              </tr>
              {data.map(item => <tr>
                <td>{item.name}</td>
                <td>{item.email}</td>
              </tr>)}
        </table>
    )
}

In conclusion

Identifying the type of component we are creating helps us think about how we want to write it, but always with the idea in mind that the UI will be governed by the state.

And sometimes, thinking about building a single screen can end up being a lot of components working together, each with its responsibilities and not knowing anything beyond what it needs, updating themselves only when they need to reinterpret based on changes in the state data.

🐶

Further Reading

For a broader perspective on how to handle application logic and testing, check out Software Test Automation.

You can also explore an Introduction to Functional Programming to see how certain programming paradigms support clear and modular code, which is highly relevant to React components.

If you’re looking to improve collaboration and knowledge-sharing, consider Mentoring as an Alternative to Courses.

And finally, for tips on leveling up your projects, these Software Solutions for Small Businesses might be a good starting point to explore outsourcing and scaling strategies.

Rodrigo Perrote

About Rodrigo Perrote

In my 10 years of experience as a front-end developer, I have consistently found myself in a perpetual state of learning, refining how I want to build user interfaces and writing numerous lines of code in the process.

WhatsApp