Class-based Components

김민경·2023년 2월 8일
0

FE(WEB) - React

목록 보기
10/13

🎈 What is & Why use class-based component?

  • 초기 React에서는 class-based component를 썼다
  • 함수형 component로는 state와 side effect를 다루지 못 했기 때문
  • 하지만 16.8v부터 Hook 개념이 등장하면서 functional component를 쓰는 방식이 일반적이게 되었다

🎈 Working with class-based components

User.js

// using functional component
const User = (props) => {
	return <li className={classes.user}> {props.name} </li>;
}

export default User;
// using class-based component
import { Component } from 'react'

class User extends Component {
	render() {
    	return <li className={classes.user}> {this.props.name} </li>;
    }
}

export default User;

working with states & events

Users.js

const DUMMY_USERS = [
	{ id: 'u1', name : 'Max' },
    { id: 'u2', name : 'Manuel' },
    { id: 'u3', name : 'Julie' },
]

class Users extends Component {
	// defining state ... use constructor()
    constructor() {
    	// need to use super()
        // because it is extending a component
        super();
    
    	// the property name should be "state"
        // have to group all the states into one object
    	this.state = {
        	showUsers : true,
            more : 'Test',
        };
    }

	// instead of implementing as function 
    // use method(메서드)
    toggleUsersHandler() {
    	// this.state.showUsers = false // NOT!
        // use this.setState
        // it always takes an object
        // react will merge it to the original state
        // it won't override the original state, different to functional state
        this.setState((curState) => {
        	return {showUsers : !curState.showUsers}
        });
    }
	
	render() {
    	const usersList = (
        	<ul>
            	{DUMMY_USERS.map((user) => (
                	<User key = {user.id} name ] {user.name} />
                ))}
            </ul>
        )
    	
        // to use method and state
        // use this.state, this.props, this.method ... 
        
        // .bind ... the this keyword inside the method(toggleUsersHanlder)
        // is now set to have the same context or the same value
        // when the below code is evaluated
    	return(
        	<div className = {classes.users}>
            	<button onClick = {this.toggleUsersHanlder.bind(this)}>
                	{this.state.showUsers ? 'Hide' : 'Show'} Users
                </button>
                {this.state.showUsers && usersList}
            </div>
        )
    }
}

Class-based Component Lifecycle

UserFinder.js

const DUMMY_USERS = [
	{ id: 'u1', name : 'Max' },
    { id: 'u2', name : 'Manuel' },
    { id: 'u3', name : 'Julie' },
]

class UserFinder extends Component {
	constructor() {
    	super();
    	this.state = {
        	filteredUsers : [],
           	searchTerm : '' 
        };
    }
    
    // same as useEffect(() => {}, [])
    ⭐ componentDidMount() {
    	// Send http request...
        this.setState({filteredUsers : DUMMY_USERS})
    }
    
    // same as useEffect(()=>{setfilteredUsers()}, [searchTerm])
    ⭐ componentDidUpdate(prevProps, prevState) {
    	if (prevState.searchTerm !== this.state.searchTerm) {
        	this.setState({
        		filteredUsers : DUMMY_USERS.filter((user) =>
            		user.name.includes(this.state.searchTerm)
            	),
        	});
        }
    	this.setState({
        	filteredUsers : DUMMY_USERS.filter((user) =>
            	user.name.includes(searchTerm)
            ),
        });
    }
    
    searchChangeHandler(event) {
    	this.setState({searchTerm : event.target.value})
    }
    
    render() {
    	return (
        	<Fragment>
            	<div className = {classes.finder}>
                	<input type = 'search' 
                    onChange = {this.searchChangeHandler.bind(this)}/>
                </div>
                <Users users = {this.state.filteredUsers} />
            </Fragment>
        )
    }
}

User.js

class User extends Component {
	
    // it executes when the component is unmounted
    ⭐ componentWillUnmount() {
    	console.log('User will unmount')
    }

	render() {
    	return <li className={classes.user}> {this.props.name} </li>;
    }
}

class-based components and context

the way to provide a context is same

const UserContext = React.createContext({
	users : []
});
const DUMMY_USERS = [
	{ id: 'u1', name : 'Max' },
    { id: 'u2', name : 'Manuel' },
    { id: 'u3', name : 'Julie' },
]

function App () {
	const userContext = {
    	users : DUMMY_USERS
    }
    
    return(
    	<UsersContext.Provider value = {usersContext}>
        	<UserFinder />
        </UsersContext.Provider>
    )
}

the way to use a context changes

  • with useContext(), you can listen to multiple context by calling useContext multiple times
  • this isn't possible for class-based components
  • you can only connect a class-based component with one context
// First way : use .Consumer
class UserFinder extends Component {
	⭐ static contextType = UsersContext

	constructor() {
    	super();
    	this.state = {
        	filteredUsers : [],
           	searchTerm : '' 
        };
    }
    
    // same as useEffect(() => {}, [])
    componentDidMount() {
        this.setState({filteredUsers : ⭐ this.context.users})
    }
    
    // same as useEffect(()=>{setfilteredUsers()}, [searchTerm])
    componentDidUpdate(prevProps, prevState) {
    	if (prevState.searchTerm !== this.state.searchTerm) {
        	this.setState({
        		filteredUsers : ⭐ this.context.users.filter((user) =>
            		user.name.includes(this.state.searchTerm)
            	),
        	});
        }
    	this.setState({
        	filteredUsers : DUMMY_USERS.filter((user) =>
            	user.name.includes(searchTerm)
            ),
        });
    }
    
    searchChangeHandler(event) {
    	this.setState({searchTerm : event.target.value})
    }
    
    render() {
    	return (
        	<Fragment>
            	<UserContext.Consumer> 
                </UserContext.Consumer>
            	<div className = {classes.finder}>
                	<input type = 'search' 
                    onChange = {this.searchChangeHandler.bind(this)}/>
                </div>
                <Users users = {this.state.filteredUsers} />
            </Fragment>
        )
    }
}

Error boundaries

sometimes there are errors which you can't prevent

(Ex) Http request being sent
if the server is temporarily not responding, the request can't complete
and you will likely end up with an error being generated

if the error is triggered in the component and can't handle it in that component(when it should be handled in a parent component), we can't use try-catch syntax. We have to use error boundaries.

by using Error boundaries, you can ensure that not your entire application crashes if something goes wrong, but that instead you can catch those errors and then handle them in an elegant way

❗❗ Error boundaries only works with class-based components, not with functional components

UserFinder.js

class UserFinder extends Component {
    
    componentDidUpdate() {
    	// triggers Error (in the child component)
    	if (this.props.users.length === 0) {
        	throw new Error('No users provided!');
        }
    }
   
    render() {
    	return (
        	<Fragment>
           		...
            </Fragment>
        )
    }
}

ErrorBoundary.js

class ErrorBoundary extends Component {
	// using constructor is optional
    // just a example that it can be coded
    // like a regular class-based-component
    constructor() {
    	super()
        this.state = { hasError : false };
    }
	
	⭐ componentDidCatch(error) {
    	console.log(error);
    	this.setState({ hasError : true });
    }
    
    render() {
    	if (this.state.hasError) {
        	// can render a error message when error occurs
        	return <p> Something went wrong! </p>
        }
    	return ⭐ this.props.children;
    }
}

export default ErrorBoundary;

UserFinder.js


class UserFinder extends Component {
	// some codes...
    
    render() {
    	return (
        	<Fragment>
            	<div className = {classes.finder}>
                	<input type = 'search' 
                    onChange = {this.searchChangeHandler.bind(this)}/>
                </div><ErrorBoundary>
                	<Users users = {this.state.filteredUsers} />
                </ErrorBoundary>
            </Fragment>
        )
    }
}

Classes-based Vs Functional Components

profile
🏛️❄💻😻🏠

0개의 댓글