React Context API로 작업하기
게시 됨: 2022-03-11React Context API는 얼마 동안 실험적인 기능으로 사용되었지만 React의 버전 16.3.0에서만 프로덕션에서 사용하기에 안전해졌습니다. 아래 기사에서는 두 가지 기본 웹 스토어 앱을 보여줍니다. 하나는 Context API로 빌드되고 다른 하나는 API 없이 빌드됩니다.
이 새로운 API는 한 가지 주요 문제인 소품 드릴 을 해결합니다. 이 용어에 익숙하지 않더라도 React.js 앱에서 작업한 적이 있다면 한 번쯤은 들어보셨을 것입니다. Prop 드릴링은 데이터를 중간 React 구성 요소의 여러 레이어를 통해 전달하여 구성 요소 A에서 구성 요소 Z로 데이터를 가져오는 처리입니다. Component는 props 를 간접적으로 받고 React 개발자는 모든 것이 제대로 작동하는지 확인해야 합니다.
React Context API 없이 일반적인 문제를 처리하는 방법을 살펴보겠습니다.
앱.js
class App extends Component { state = { cars: { car001: { name: 'Honda', price: 100 }, car002: { name: 'BMW', price: 150 }, car003: { name: 'Mercedes', price: 200 } } }; incrementCarPrice = this.incrementCarPrice.bind(this); decrementCarPrice = this.decrementCarPrice.bind(this); incrementCarPrice(selectedID) { // a simple method that manipulates the state const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price + 1; this.setState({ cars }); } decrementCarPrice(selectedID) { // a simple method that manipulates the state const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price - 1; this.setState({ cars }); } render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to my web store</h1> </header> {/* Pass props twice */} <ProductList cars={this.state.cars} incrementCarPrice={this.incrementCarPrice} decrementCarPrice={this.decrementCarPrice} /> </div> ); } }
제품 목록 .js
const ProductList = props => ( <div className="product-list"> <h2>Product list:</h2> {/* Pass props twice */} <Cars cars={props.cars} incrementCarPrice={props.incrementCarPrice} decrementCarPrice={props.decrementCarPrice} /> {/* Other potential product categories which we will skip for this demo: */} {/* <Electronics /> */} {/* <Clothes /> */} {/* <Shoes /> */} </div> ); export default ProductList;
자동차.js
const Cars = props => ( <Fragment> <h4>Cars:</h4> {/* Finally we can use data */} {Object.keys(props.cars).map(carID => ( <Car key={carID} name={props.cars[carID].name} price={props.cars[carID].price} incrementPrice={() => props.incrementCarPrice(carID)} decrementPrice={() => props.decrementCarPrice(carID)} /> ))} </Fragment> );
자동차.js
const Cars = props => ( <Fragment> <p>Name: {props.name}</p> <p>Price: ${props.price}</p> <button onClick={props.incrementPrice}>↑</button> <button onClick={props.decrementPrice}>↓</button> </Fragment> );
물론 이것이 데이터를 처리하는 가장 좋은 방법은 아니지만 소품 드릴링 이 왜 안 좋은지 보여주기를 바랍니다. 그렇다면 Context API는 이것을 피하는 데 어떻게 도움이 될 수 있습니까?
컨텍스트 웹 스토어 소개
앱을 리팩토링하고 무엇을 할 수 있는지 시연해 보겠습니다. 간단히 말해서 Context API를 사용하면 데이터가 있는 중앙 저장소를 가질 수 있습니다(예, Redux에서처럼). 저장소는 모든 구성 요소에 직접 삽입할 수 있습니다. 중개자를 끊을 수 있습니다!
리팩토링은 매우 쉽습니다. 구성 요소가 구조화되는 방식을 변경할 필요가 없습니다. 그러나 공급자와 소비자라는 몇 가지 새로운 구성 요소를 만들어야 합니다.
1. 컨텍스트 초기화
먼저 컨텍스트를 생성해야 하며, 이는 나중에 공급자와 소비자를 생성하는 데 사용할 수 있습니다.

MyContext.js
import React from 'react'; // this is the equivalent to the createStore method of Redux // https://redux.js.org/api/createstore const MyContext = React.createContext(); export default MyContext;
2. 공급자 생성
완료되면 컨텍스트를 가져와서 이를 사용하여 MyProvider라고 하는 공급자를 생성할 수 있습니다. 그 안에서 우리는 당신이 우리의 공급자 컴포넌트를 통해 공유할 수 있는 몇 가지 값 으로 상태를 초기화합니다. 이 예에서는 상태를 조작하는 몇 가지 메서드와 함께 this.state.cars
를 공유하고 있습니다. 이러한 방법을 Redux의 감속기로 생각하십시오.
MyProvider.js
import MyContext from './MyContext'; class MyProvider extends Component { state = { cars: { car001: { name: 'Honda', price: 100 }, car002: { name: 'BMW', price: 150 }, car003: { name: 'Mercedes', price: 200 } } }; render() { return ( <MyContext.Provider value={{ cars: this.state.cars, incrementPrice: selectedID => { const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price + 1; this.setState({ cars }); }, decrementPrice: selectedID => { const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price - 1; this.setState({ cars }); } }} > {this.props.children} </MyContext.Provider> ); } }
공급자가 다른 구성 요소에 액세스할 수 있도록 하려면 앱을 해당 구성 요소로 래핑해야 합니다(예, Redux에서와 같이). 현재 MyProvider.js에 정의되어 있기 때문에 상태와 메서드를 제거할 수 있습니다.
앱.js
class App extends Component { render() { return ( <MyProvider> <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to my web store</h1> </header> <ProductList /> </div> </MyProvider> ); } }
3. 소비자 생성
컨텍스트를 다시 가져와서 구성 요소에 컨텍스트 인수를 삽입하는 구성 요소를 랩핑해야 합니다. 그 후에는 꽤 직선적입니다. props 를 사용하는 것과 같은 방식으로 context 를 사용합니다. 여기에는 MyProducer에서 공유한 모든 값이 들어 있으므로 사용하기만 하면 됩니다!
자동차.js
const Cars = () => ( <MyContext.Consumer> {context => ( <Fragment> <h4>Cars:</h4> {Object.keys(context.cars).map(carID => ( <Car key={carID} name={context.cars[carID].name} price={context.cars[carID].price} incrementPrice={() => context.incrementPrice(carID)} decrementPrice={() => context.decrementPrice(carID)} /> ))} </Fragment> )} </MyContext.Consumer> );
우리는 무엇을 잊었습니까? 제품 목록! 여기서 이점이 분명해집니다. 우리는 어떤 데이터나 방법도 전달하지 않습니다. 구성 요소는 몇 가지 구성 요소만 렌더링하면 되므로 단순화됩니다.
제품 목록.js
const ProductList = () => ( <div className="product-list"> <h2>Product list:</h2> <Cars /> {/* Other potential product categories which we will skip for this demo: */} {/* <Electronics /> */} {/* <Clothes /> */} {/* <Shoes /> */} </div> );
이 기사를 진행하면서 Redux와 Context API를 몇 가지 비교했습니다. Redux의 가장 큰 장점 중 하나는 앱이 모든 구성 요소에서 액세스할 수 있는 중앙 저장소를 가질 수 있다는 사실입니다. 새로운 Context API를 사용하면 기본적으로 해당 기능을 사용할 수 있습니다. Context API가 Redux를 쓸모없게 만들 것이라는 많은 과장이 있었습니다.
이는 중앙 저장소 기능으로만 Redux를 사용하는 사람들에게 해당될 수 있습니다. 그것이 당신이 그것을 사용했던 유일한 기능이라면 이제 Context API로 교체하고 타사 라이브러리를 사용하지 않고 소품 드릴을 피할 수 있습니다.
React 애플리케이션의 성능을 측정하고 최적화하는 데 관심이 있다면 동료 Toptaler William Wang 의 A Guide to Optimizing React Performance를 읽어보세요.