React Uh-Ohs for Dummies

Jayed Talukder
3 min readOct 30, 2023

--

1. Using number in the ‘to’ prop of the <Link> component

(assuming that you are using “react-router-dom” for routing)

Imagine you are creating an app where there can be a list page, and there can be a detail page with dynamic values for a parameter, namely ‘id’ here.

For a router setup for something like the following-

const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
errorElement: <ErrorPage />,
children: [
{
index: true,
element: <HomePage />
},
{
path: 'task-list',
element: <TaskListPage />
},
{
path: 'task-list/:id',
element: <TaskDetailPage />
}
]
}
]);

and for a list page/component that looks like the following-

import {Link} from "react-router-dom";

const TaskListPage = () => {
const tasks = [
{
id: 1,
title: 'Task A'
},
{
id: 2,
title: 'Task B'
}
];

return <>
<h2>task list</h2>
<ul>
{tasks.map(task => <li key={task.id}>
<Link to={task.id}> {task.title}</Link>
</li>
)}
</ul>
</>
}

export default TaskListPage;

The routing (clicking on the anchor tag generated by the <Link> ) will not work.

It’s because, the prop to expects a string, and as of the version that I am using, “react-router-dom”: “^ 6.17.0”, it can’t interpret this as a valid path.

Solution:

Either, do not use number value in that prop, or use a template literal to ensure that task.id is being converted to a string.

<Link to={`${task.id}`}>{task.title}</Link>

2. Using class instances in the state

“A non-serializable value was detected in the state”

One possibility can be having something like this in the reducer-

            state.tasks.push(new Task(action.payload));

The task class looks like the following-

class Task {
public id: string;
constructor(public title: string) {
this.id = new Date().toISOString();
}
}

export default Task;

The Redux Style guide explicitely states that

“Avoid putting non-serializable values such as Promises, Symbols, Maps/Sets, functions, or class instances into the Redux store state or dispatched actions”

https://redux.js.org/style-guide/#do-not-put-non-serializable-values-in-state-or-actions

The workaround can be following:

interface ITask {
id: string;
title: string
}

export default ITask;
            const newTask: ITask = {
id: new Date().toISOString(),
title: action.payload
};
state.tasks.push(newTask); // can be made inline, only for demonstration

Or using plain and simple objects.

3. Replacing every variable with state, using the hook ‘useState’

You should only use the hook useState when you need the value to be updated in a re-render.

If you have a component, that merely displays a data, never updates it- that can stay as a standard variable.

Because, there is no possibility of that data being updated in the view once the initial render happens.

import React, {useState} from "react";

const Tag: React.FC<{tag: string}> = (props) => {
const [tag, setTag] = useState(props.tag)

return <>{tag}</>;
}

export default Tag;

can be replaced with

import React from "react";

const Tag: React.FC<{tag: string}> = (props) => {
return <>{props.tag}</>;
}

export default Tag;

4.

To be continued… :)

--

--