Let’s start a new app to organize family history using ReactJS and TypeScript.
Why am I doing this?
Today I was fiddling with some family history records and wondered, “Why don’t I write an app to help me organize this?”
I started to write it using just JavaScript, then thought, “I should write it in TypeScript so I’ll better understand it.”
Then as I wrote in TypeScript, I realized that rendering data to the DOM was a bit tedious, so I decided to use React JS as well. This way I’ll learn two tools in order to use them.
Introducing my sixth great-grandfather
Henry Michael Whittenberg is one of my sixth great-grandfathers, which I think means he goes back seven generations. Anyway, his data is what I’m starting with:
export interface IPerson {
name: string;
gender: string;
birth: Date;
death: Date;
}
const person: Person = {
name: "Henry Michael Whittenberg”,
gender: "Male”,
birth: new Date("1710-01-01”),
death: new Date("1767-01-19"),
};
So I have an interface called IPerson, where I have a basic structure for each person in my family history. Of course, there’s going to be some issues I’ll likely run into, such as if there’s a person where I don’t know the birthdate or some other fact. But I can revise that later. For today, I’m starting with someone I have some established facts about (although I question the accuracy of his birthdate).
Rendering the app
The app is fairly simple using React:
function App() {
return (
<main className="App”>
<Person person={person} />
</main>
);
}
This renders a Person component, using person as the props.
const Person = (props: { person: IPerson }) => {
const { person } = props;
return (
<dl>
{Object.entries(person).map((fact) => (
<Fact fact={fact} key={fact[0].toString()} />
))}
</dl>
);
};
For now, the Person component renders each of the facts using the Fact component. BTW, the component names are why I prefixed the interfaces with an I (e.g. IPerson, IFact, etc.)
Finally there’s the Fact component:
interface IFact {
string: string | Date;
}
const Fact = (props: { fact: IFact[] }) => {
const [key, value] = props.fact;
return (
<>
<dt>{key}</dt>
{value instanceof Date && <dd>{value.toLocaleDateString()}</dd>}
{typeof value === "string" && <dd>{value}</dd>}
</>
);
};
The main reason I created a Fact component was to handle rendering the dates. In theory, I could use the .toLocaleString() method on almost any data type, but I wanted to be able to customize the way dates appear.