React Context API로 작업하기

게시 됨: 2022-03-11

React 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}>&uarr;</button> <button onClick={props.decrementPrice}>&darr;</button> </Fragment> );

물론 이것이 데이터를 처리하는 가장 좋은 방법은 아니지만 소품 드릴링 이 왜 안 좋은지 보여주기를 바랍니다. 그렇다면 Context API는 이것을 피하는 데 어떻게 도움이 될 수 있습니까?

컨텍스트 웹 스토어 소개

앱을 리팩토링하고 무엇을 할 수 있는지 시연해 보겠습니다. 간단히 말해서 Context API를 사용하면 데이터가 있는 중앙 저장소를 가질 수 있습니다(예, Redux에서처럼). 저장소는 모든 구성 요소에 직접 삽입할 수 있습니다. 중개자를 끊을 수 있습니다!

두 가지 상태 흐름의 예: 하나는 React Context API를 사용하고 다른 하나는 사용하지 않습니다.

리팩토링은 매우 쉽습니다. 구성 요소가 구조화되는 방식을 변경할 필요가 없습니다. 그러나 공급자와 소비자라는 몇 가지 새로운 구성 요소를 만들어야 합니다.

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를 읽어보세요.