diff --git a/Programming_Languages/React/Hooks/Forms.md b/Programming_Languages/React/Hooks/Forms.md new file mode 100644 index 0000000..823a4de --- /dev/null +++ b/Programming_Languages/React/Hooks/Forms.md @@ -0,0 +1,144 @@ +--- +tags: + - Programming_Languages + - javascript + - react + - react-hooks +--- + +# Forms using hooks + +With hooks, form processing is exactly the same as [classes](/Programming_Languages/React/Classes/Forms.md) in terms of the overall methodology, but the syntax is slightly different as a result of the `useState` hook. + +> + +## Basic approach + +Instead of using `this.state` and `this.setState` . We just have the `useState` hook. But the controlled component principle is the same. Let's say we have a simple email input: + +```jsx +const [email, setEmail] = useState(''); +``` + +As this is a form, the state change is going to be the result of user input. So we need to prep our form to enable this. + +```html + +``` + +Now we just need to make good on the `setEmail` method we declared when we initialised the state: + +```jsx +const handleChange = (event) => { + setEmail(event.target.value); +}; +``` + +### Applied example + +Here is an applied example of the above approach for a form that has three input fields. This component outputs the inputs as JSON on submit: + +```jsx + +function FormHook() { + const [email, setEmail] = useState(""); + const [phone, setPhone] = useState(""); + const [age, setAge] = useState(""); + const [formOutput, setFormOutput] = useState("Form output"); + + const handleSubmit = (event) => { + event.preventDefault(); + setFormOutput( + JSON.stringify({ email: email, phone: phone, age: age }, null, 2) + ); + }; + return ( +
+ setEmail(event.target.value)}> + setPhone(event.target.value)}> + setAge(event.target.value)}> + +
+ ) +}; +``` + +## More complex forms + +The above is fine if you only have one form with a couple of inputs. But if you are managing multiple forms or forms with a complex array of inputs, you would need to create `useState` declaration for every single input with a custom `onChange` event for each one which is repetitious and not very clean. + +So instead of this, just like with class-based controlled components, we use the `name` HTML attribute to distinguish each input and create a generic `onChange` function that distinguishes each separate input by destructuring a key, value object using the `name`. + +```jsx +
+ + + + +
+``` + +```jsx +const initialState = { + email: '', + phone: '', + age: '', +}; + +const [formValues, setFormValues] = useState(initialState); + +const handleChange = (event) => { + const {name, value} = event.target; + setFormValues({...formValues, [name]: value}); +}; +``` + +There are three parts: + +1. First we create the initial state. +2. Next we store this initial state as the variable in the `useState` initialisation: `formValues` . We also provide a method `setFormValues` which will be used by the change handler to log the user's inputs. +3. Finally we create the function that will log the user changes. First we use object destructuring on the change event to enable us to retrieve the `name` and `value` attributes of the HTML inputs in the component. Then we use spread syntax to say that for each input pair, retrieve its value, using the destructured `name` variable as the key. + +### Applied example + +Below I have updated the previous context to this time reflect the new, abstracted logic: + +```jsx +function FormHookAbstracted() { + const initialState = { + email: "", + phone: "", + age: "", + }; + const [formValues, setFormValues] = useState(initialState); + const handleChange = (event) => { + const { name, value } = event.target; + setFormValues({ ...formValues, [name]: value }); + }; + const handleSubmit = (event) => { + event.preventDefault(); + setFormOutput( + JSON.stringify({ email: email, phone: phone, age: age }, null, 2) + ); + }; + return ( +
+ + + + +
+ ) +}; +export default FormHookAbstracted; +``` + +Note that instead of individual variables `email` , `phone`, `age` , this approach returns a single object `formValues` . We could therefore access the individual values with e.g `[formValues.email](http://formvalues.email)` . + +As it is an object, it makes resetting to the original state very easy, viz: + +```jsx +const handleReset = () => { + Object.values(formValues).map((x) => setFormValues(initialState)); +}; +``` diff --git a/Programming_Languages/React/React_Typescript/Events.md b/Programming_Languages/React/React_Typescript/Events.md new file mode 100644 index 0000000..4507d8e --- /dev/null +++ b/Programming_Languages/React/React_Typescript/Events.md @@ -0,0 +1,73 @@ +--- +tags: + - Programming_Languages + - typescript + - react +--- + +# Events + +Building on the previous examples for React TypeScript we are going to add a simple form that enables the user to add people to the list. This will demonstrate how we type components that use event handlers. + +We are going to use the preexisting interface for recording the list items: + +```tsx +interface IState { + people: { + name: string; + age: number; + }[]; +} +``` + +Our form: + +```ts +import {IState as Props}; +``` + +```tsx + +interface IProps { + people: Props["people"] + setPeople: React.Dispatch> +} + +const AddToList: React.FC = () => { + const [people, setPeople] = useState({}) + const [formVals, setFormVals] = useState({}); + + const handleChange = (e: React.ChangeEvent): void => { + setFormValues({ + ...input, + [e.target.name]: e.target.value, + }); + }; + + const handleClick = (): void => { + if (!input.name || !input.age) return + + setPeople({ + ...people, + { + name: input.name, + age: input.age + } + }) + } + +return ( +
+ + +
+ +); +}; +``` + +This follows standard practise for [controlled-components](/Programming_Languages/React/Hooks/Forms.md). The TS specific additions: + +- We define the change event as being of the type `React.ChangeEvent` and state that it corresponds to a generic - `HTMLInputElement`. So we are saying that whenever this function is called we must be passing it an input element so that we can extract the event associated with its `target` property. + +- We are passing around variations on the `IState` interface in order to type the values that we are adding to the people array. diff --git a/Programming_Languages/React/React_Typescript/Functions.md b/Programming_Languages/React/React_Typescript/Functions.md new file mode 100644 index 0000000..8afc319 --- /dev/null +++ b/Programming_Languages/React/React_Typescript/Functions.md @@ -0,0 +1,39 @@ +--- +tags: + - Programming_Languages + - typescript + - react +--- + +# Functions + +Continuing from the other examples of React Typescript, we could do standard listing function, like: + +```tsx +
    + {people.map((person) => { + return
  • {person.name}
  • ; + })} +
+``` + +But it's neater to do it with a function defined within the `List` component: + +```tsx +const renderList = (): JSX.Element[] => { + return people.map((person) => { + return ( +
  • +
    {person.name}
    +
    {person.age}
    +
  • + ); + }); +}; +``` + +And then change the eariler list to a function invocation: + +```tsx +
      {renderList()}
        +``` diff --git a/Programming_Languages/React/React_Typescript/Managing_state.md b/Programming_Languages/React/React_Typescript/Managing_state.md new file mode 100644 index 0000000..7986dd2 --- /dev/null +++ b/Programming_Languages/React/React_Typescript/Managing_state.md @@ -0,0 +1,40 @@ +--- +tags: + - Programming_Languages + - typescript + - react +--- + +# Managing state + +## Basic: `useState` + +```tsx +const [amount, setAmount] = useState(3); +``` + +### Custom type + +```tsx +interface IState { + people: IPerson[]; +} + +interface IPerson { + name: string; + age: number; +} + +const [people, setPeople] = useState({}); + +// Alternative declaration + +interface IState { + people: { + name: string; + age: number; + }[]; +} + +const [people, setPeople] = useState({}); +``` diff --git a/Programming_Languages/React/React_Typescript/Props.md b/Programming_Languages/React/React_Typescript/Props.md new file mode 100644 index 0000000..892f717 --- /dev/null +++ b/Programming_Languages/React/React_Typescript/Props.md @@ -0,0 +1,38 @@ +--- +tags: + - Programming_Languages + - typescript + - react +--- + +# Props + +```tsx +interface IProps { + people: { + name: string; + age: number; + note?: string; + }[]; +} + +const List: React.FC = ({people}: IProps) => { + return() +} + +// Note we say that the props into the func component are of type IProps +// And we destructure the people key + +``` + +Then in the parent: + +```tsx + +const [people, setPeople] = useState({}); + + + +``` + +

        Should I use type or interface? What is consensus?

        diff --git a/Programming_Languages/TypeScript/Primitive_types.md b/Programming_Languages/TypeScript/Primitive_types.md index ef07bef..d2746ce 100644 --- a/Programming_Languages/TypeScript/Primitive_types.md +++ b/Programming_Languages/TypeScript/Primitive_types.md @@ -34,3 +34,29 @@ const store: string[] = []; // Empty array `Object` is a valid type declaration in TS but it is not particularly helpful since it becomes similar to using [any](./Any.md) given that most primitive types in JavaScripts prototypically inherit from an Object. Generally, when you use objects in TypeScript you type them as [custom types](./Custom_types.md). + +## Array of (untyped) objects + +If we just know that it is going to be an array of objects we can use: + +```ts +const arrOfObj = {}[] +``` + +If we wish to define a particular shape but without defining a type: + +```ts +const arrOfObj = { name: string, age: number }[] +``` + +But better for reusability to do: + +```ts +type ArrayOfObj = { + name: string, + age: number +} + +const arrOfObj: ArrayOfObj[] = [{}, ...] + +```