Table of contents
import React, { FunctionComponent } from "react";
const Hello: FunctionComponent = () => {
return <div>Hallo</div>;
};
export default Hello;
import React, { FunctionComponent } from "react";
// Children's type is inferred by the FunctionComponent interface
const Hello: FunctionComponent = ({ children }) => {
return <div>{children}</div>;
};
export default Hello;
<Hello>
<span>This span will be displayed, where children is used in the 'Hello' component</span>
<Hello>
import React, { FunctionComponent } from "react";
// Type definitions for our prop bindings
interface HelloProps {
name: string;
}
// Functions can infer interfaces in typescript
// We are passing props as an object with the `{}` syntax
// Inside are all properties defined by our HelloProps interface
const Hello: FunctionComponent<HelloProps> = ({ name }) => {
return <div>Hello, {name}</div>;
};
export default Hello;
interface ExampleProps {
requiredProp: number;
optionalProp?: string;
optionalPropWithDefault?: string;
}
const Example: FunctionComponent<ExampleProps> = ({
requiredProp,
optionalProp,
optionalPropWithDefault = "Default value"
}) => {
return (
<ul>
<li>{requiredProp}</li>
{optionalProp && <li>{optionalProp}</li>}
<li>{optionalPropWithDefault}</li>
</ul>
);
};
While react does not have a templating engine itself, common templating tasks can be easily achieved by using tsx.
const Hello: FunctionComponent = () => {
const condition = 1 > 2;
return (
<div>
<div>I'm always shown</div>
{condition && <div>Only shown if condition is true</div>}
</div>
);
};
const Hello: FunctionComponent = () => {
const condition = 1 > 2;
return (
<div>
<div>I'm always shown</div>
{condition ? <div>Condition is true</div> : <div>Condition is false</div>}
</div>
);
};
const Hello: FunctionComponent = () => {
let output = null;
if (1 > 2) {
output = <div>Condition is true</div>;
} else {
output = <div>Condition is false</div>;
}
return (
<div>
<div>I'm always shown</div>
{output}
</div>
);
};
return (
<div>
<div>I'm always shown</div>
{/* We can use a function to put the condition inside the "template" */}
{(() => {
if (1 > 2) {
return <div>Condition is true</div>;
} else {
return <div>Condition is false</div>;
}
})()}
</div>
);
};
First it's important to understand that react simply renders arrays as individual elements.
<ul>{[<li>One</li>, <li>Two</li>, <li>Three</li>]}</ul>
will be rendered as:
- One
- Two
- Three
We can make use of that by using the map
function to transform our data into markup, as map
always returns an array.
const Hello: FunctionComponent = ({}) => {
const todos = ["Todo 1", "Todo 2", "Todo 3"];
return (
<ul>
{todos.map(todo => <li>{todo}</li>)}
</ul>
);
};
const Hello: FunctionComponent = ({}) => {
const todos = [{ task: "Todo 1", done: false }, { task: "Todo 2", done: true }];
return (
<ul>
{todos.map(todo => {
if (todo.done) {
return <li className="todo done">[DONE] {todo.task}</li>;
} else {
return <li className="todo">{todo.task}</li>;
}
})}
</ul>
);
};
We are using react hooks for everything that's state / lifecycle related. Because hooks only work with functional components, we are solely using FunctionComponent
instead of React.Component
or classes.
const Counter: FunctionComponent = ({}) => {
const [counter, setCounter] = useState(0);
return (
<div>
{counter}
<button onClick={() => setCounter(counter => counter + 1)}>}>+</button>
</div>
);
};
Functional components and hooks don't have a concept of lifecycles
but we can get something similar using the useEffect hook, which runs every time the component is re-rendered.
// Without dependency array
useEffect(() => {
console.log("Component will rerender");
});
// With empty dependency array
useEffect(() => {
console.log("Component will mount");
}, []);
// With dependency array
useEffect(() => {
console.log("Watching property 'counter'");
}, [counter]);
// With dependency array
useLayoutEffect(() => {
console.log("Component did render");
});
// With dependency array
useLayoutEffect(() => {
console.log("Componentn did mount");
}, []);
interface Command<StateType> {
execute(state: StateType): StateType;
}
type MyState = {
counter: number;
message: string;
};
class IncreaseCounterCommand implements Command<MyState> {
execute(currentState: MyState) {
// Always create a new object, never change existing state
return {
...currentState,
counter: currentState.counter++
};
}
}
class ChangeMessageCommand implements Command<MyState> {
constructor(private msg: string) {}
execute(currentState: MyState) {
return {
...currentState,
message: this.msg
};
}
}
const myStateReducer = (state: MyState, command: Command<MyState>) => command.execute(state);
const initialState: MyState = { counter: 0, message: "Hello" };
const Test: FunctionComponent = () => {
const [state, dispatch] = useReducer(myStateReducer, initialState);
return (
<div>
Counter: {state.counter}
Message: {state.message}
<button onClick={() => dispatch(new IncreaseCounterCommand())}>Increase Counter</button>
<button onClick={() => dispatch(new ChangeMessageCommand("π"))}>Change Message</button>
</div>
);
};
{
"Create a FunctionComponent": {
"prefix": "rfc",
"body": [
"import React, { FunctionComponent } from \"react\";",
"",
"interface $1Props {}",
"",
"const $1: FunctionComponent<$1Props> = ({}) => {",
"\treturn null;",
"}",
"export default $1;"
],
"description": "Creates a functional React component with the corresponding interface and exports."
}
}
{
"Create a Jest Unit Test": {
"prefix": "jut",
"body": [
"import React from \"react\";",
"import { shallow } from \"enzyme\";",
"import $1 from \"./$1\";",
"",
"describe(\"$1\", ()=> {",
" it(\"should render to snapshot\", () => {",
" const component = shallow(<$1 />);",
" expect(component).toMatchSnapshot();",
" });",
"});"
]
}
}