Fragments, Portals and Refs

Fragments, Portals and Refs

This is a note for a great React course on Udemy developed by Maximilian Schwarzmüller

Demo 3: Simple User (Fragments, Portals, Refs)

Completed code in Github Repository

Fragment

It's an empty wrapper Component.

It doesn't render any real HTML element to the DOM. But it fulfills React's/JSX requirement.

import Fragment from 'react'; return ( <Fragment> <AddUser onAddUser={addUserHandler} /> <UsersList users={usersList} /> </Fragment> );

Limitation of JSX

  • Cannot return more than one "root" JSX element. (Because of React.createElement)

    Solution: Always Wrap Adjacent Element.

  • Another problem: "div soup". In bigger app, tons of unnecessary div wrapper.

    Solution: Using Wrapper Comp.

const Wrapper = (props) => props.children; export default Wrapper;

Portals

  • Modal(Side-drawer, Dialogs...) Problem: Semantically and from a "Clean HTML" perspective, having a nested modal isn't ideal. It is an overlay to the entire page after all.

    p.s. div with onClick event: work but not a good practice.

    Solution: Portal(render the modal at specific position in HTML).

<!-- In index.html --> <body> <div id="backdrop-root"></div> <div id="overlay-root"></div> <div id="root"></div> </body>
const ErrorModal = (props) => { const { onConfirm, title, message } = props; return ( <> {ReactDOM.createPortal( <Backdrop onConfirm={onConfirm} />, document.getElementById('backdrop-root') )} {ReactDOM.createPortal( <ModalOverlay title={title} message={message} onConfirm={onConfirm} />, document.getElementById('overlay-root') )} </> ); };

"ref": the second hook

  • Allows us to get access to other DOM elements (Shorten the state-based input handler)
  • Two way to handle the user input:

useRef() (uncontrolled component) AddUser.js

Only use it to read the data. Never manipulate DOM directly using it.

// Use only for read value. Leave the manipulating staff to React const nameInputRef = useRef(); const ageInputRef = useRef(); const addUserHandler = (event) => { // We don't need the state to get access to user input (state-based solution) const enteredName = nameInputRef.current.value; const enteredUserAge = ageInputRef.current.value; const [error, setError] = useState(); const addUserHandler = (event) => { event.preventDefault(); props.onAddUser(enteredName, enteredUserAge); // Important: Rarely do that ! // Do not use ref to manipulate the DOM nameInputRef.current.value = ''; ageInputRef.current.value = ''; }; }; return ( <form onSubmit={addUserHandler}> <label htmlFor="username">Username</label> <input id="username" type="text" ref={nameInputRef} /> <label htmlFor="age">Age (Years)</label> <input id="age" type="number" ref={ageInputRef} /> <Button type="submit">Add User</Button> </form> );

state-based tracker (controlled component)

const [enteredUsername, setEnteredUsername] = useState(''); const [enteredAge, setEnteredAge] = useState(''); return ( <form onSubmit={addUserHandler}> <label htmlFor="username">Username</label> <input id="username" type="text" value={enteredUsername} onChange={usernameChangeHandler} /> <label htmlFor="age">Age (Years)</label> <input id="age" type="number" value={enteredAge} onChange={ageChangeHandler} /> <Button type="submit">Add User</Button> </form> );