18. React Context

김관주·2023년 12월 16일
0

웹시스템

목록 보기
18/22

Context

Passing Data with Context

  • Context lets the parent component make some information available to any component in the tree below it—no matter how deep—without passing it explicitly through props.
  • We have been using props to pass information so far
    • However, passing props can become verbose and inconvenient when you need to pass some prop deeply through the tree, or if many components need the same prop.

Example: Heading/Section component with level

export default function Page() {
    return (
        <Section>
            <Heading level={1}>Title</Heading>
            <Section>
                <Heading level={2}>Heading</Heading>
                <Heading level={2}>Heading</Heading>
                <Heading level={2}>Heading</Heading>
                <Section>
                    <Heading level={3}>Sub-heading</Heading>
                    <Heading level={3}>Sub-heading</Heading>
                    <Heading level={3}>Sub-heading</Heading>
                    <Section>
                        <Heading level={4}>Sub-sub-heading</Heading>
                        <Heading level={4}>Sub-sub-heading</Heading>
                        <Heading level={4}>Sub-sub-heading</Heading>
                    </Section>
                </Section>
            </Section>
        </Section>
    );
}

export default function Section({ children }) {
    return (
        <section className="section">
            {children}
        </section>
    );
}

export default function Heading({ level, children }){
    switch (level) {
        case 1:
            return <h1>{children}</h1>;
        case 2:
            return <h2>{children}</h2>;
        case 3:
            return <h3>{children}</h3>;
        case 4:
            return <h4>{children}</h4>;
        case 5:
            return <h5>{children}</h5>;
        case 6:
            return <h6>{children}</h6>;
        default:
            throw Error('Unknown level: ' + level);
    }}

Context Example: Heading/Section component with level

  • Moving level prop from Heading to Section component to force that all headings in the same section have the same size
  • But how can the <Heading> component know the level of its closest <Section>? That would require some way for a child to “ask” for data from somewhere above in the tree.
    제목/섹션 컴포넌트에 레벨이라는 속성이 있습니다. 모든 제목이 같은 섹션 내에서 동일한 크기를 갖도록 하기 위해, 레벨 속성을 제목 컴포넌트에서 섹션 컴포넌트로 이동시키려고 합니다.
    하지만, <Heading> 컴포넌트는 어떻게 가장 가까운 <Section>의 레벨을 알 수 있을까요? 이는 어떤 방식으로 자식이 트리 상단의 데이터를 '요청'할 수 있는 방법을 필요로 합니다
<Section>
    <Heading level={3}>About</Heading>
    <Heading level={3}>Photos</Heading>
    <Heading level={3}>Videos</Heading>
</Section>
<Section level={3}>
    <Heading>About</Heading>
    <Heading>Photos</Heading>
    <Heading>Videos</Heading>
</Section>

Use Context and Make it Available to All the Children

  1. Create a context. (You can call it LevelContext, since it’s for the heading level.)
  2. Use that context from the component that needs the data. (Heading will use LevelContext.)
  3. Provide that context from the component that specifies the data. (Section will provide LevelContext.)

Context Example: Heading/Section component with level

  1. Create the context : createContext takes only argument of the default value
  2. Use Context : import useContext Hook as well as the created context
  • you can only call a Hook immediately inside a React component (not inside loops or conditions).
  1. Provide the context : Wrap them with a context provider to provide the LevelContext to them
  • This tells React: “if any component inside this <Section> asks for LevelContext, give them this level.” The component will use the value of the nearest

  • 상위 컴포넌트 예제

import { LevelContext } from './LevelContext.js';
export default function Section({ level, children }) {
    return (
        <section className="section">
            <LevelContext.Provider value={level}>
                {children}
            </LevelContext.Provider>
        </section>
    );
}
import { createContext } from 'react';
export const LevelContext = createContext(1);

상위 컴포넌트에서 <LevelContext.Provider value={level}> 형태로 데이터를 보내준다.

하위 컴포넌트에서 const level = useContext(LevelContext); 형태로 사용한다.

완성본은 다음과 같다.

import {createContext, useContext} from "react";
const LevelContext = createContext(1);
export default function Page() {
    return (
        <Section level={1}>
            <Heading>Title</Heading>
            <Section level={2}>
                <Heading>Heading</Heading>
                <Heading>Heading</Heading>
                <Heading>Heading</Heading>
                <Section level={3}>
                    <Heading>Sub-heading</Heading>
                    <Heading>Sub-heading</Heading>
                    <Heading>Sub-heading</Heading>
                    <Section level={4}>
                        <Heading>Sub-sub-heading</Heading>
                        <Heading>Sub-sub-heading</Heading>
                        <Heading>Sub-sub-heading</Heading>
                    </Section>
                </Section>
            </Section>
        </Section>
    );
}
 function Section({ level,children }) {
    return (
        <section className="section">
            <LevelContext.Provider value={level}>
                {children}
            </LevelContext.Provider>
        </section>
    );
}

 function Heading({ children }){
     const level = useContext(LevelContext);
    switch (level) {
        case 1:
            return <h1>{children}</h1>;
        case 2:
            return <h2>{children}</h2>;
        case 3:
            return <h3>{children}</h3>;
        case 4:
            return <h4>{children}</h4>;
        case 5:
            return <h5>{children}</h5>;
        case 6:
            return <h6>{children}</h6>;
        default:
            throw Error('Unknown level: ' + level);
    }
}

Before you use context

  • Context may be overused easily. However, it should be used selectively because:
    • Performance Considerations: When you use context, all components that are consumers of that context will re-render whenever the context value changes
    • Clarity(명료함) and Predictability: Passing data through props provides a clear and predictable flow of data in React. When you use context, it might become less obvious where certain pieces of data are coming from, especially in larger applications.
    • Component Reusability: Components that rely on context are more tightly coupled to the context provider. This reduces their reusability because they are dependent on a specific context structure.
      컴포넌트가 특정 Context에 의존하게 되면 그 컴포넌트는 해당 Context가 제공하는 데이터 구조에 강하게 결합됩니다.
      즉, 컴포넌트는 특정 Context 없이는 제대로 작동하지 않게 되므로, 다른 Context 또는 Context 없는 환경에서 재사용하기가 어려워집니다.

Alternatives you may consider before using context

  1. Start by passing props. If your components are not trivial, it’s not unusual to pass a dozen props down through a dozen components. It may feel like a slog, but it makes it very clear which components use which data! The person maintaining your code will be glad you’ve made the data flow explicit with props.
  2. Extract components and pass JSX as children to them. If you pass some data through many layers of intermediate components that don’t use that data (and only pass it further down), this often means that you forgot to extract some components along the way. For example, maybe you pass data props like posts to visual components that don’t use them directly, like <Layout posts={posts} />. Instead, make Layout take children as a prop, and render <Layout><Posts posts={posts} /></Layout>. This reduces the number of layers between the component specifying the data and the one that needs it.

REDUX

What is Redux?

  • When Should I Use Redux?
    • You have large amounts of application state that are needed in many places in the app
    • The app state is updated frequently over time
  • What is Redux Store?
    • A "store" is a container that holds your application's global state.
    • A store is a JavaScript object with a few special functions and abilities that make it different than a plain global object.
    • You must never directly modify or change the state that is kept inside the Redux store

Redux overview (Terms)

  • Subscribers are notified the changes of state by the store.
  • Reducers are functions that calculate a new state value based on previous state + an action (NOT a useReducer())
    • We can think of a reducer as an event listener which handles events based on the received action (event) type.
  • Actions are plain objects with a type field, and describe "what happened" in the app
  • Dispatch The only way to update the state is to call store.dispatch() and pass in an action object. The store will run its reducer function and save the new state value inside.

Data Flow of Redux

  • Actions are dispatched in response to a user interaction like a click
  • The store runs the reducer function to calculate a new state
  • The UI reads the new state to display the new values


Simple Counter Example: components/counterReducer.js

const counterReducer = (state= 0, action) => {
    switch (action.type) {
        case "INCREMENT":
            return state + 1;
        case "DECREMENT":
            return state - 1;
        case "RESET":
            return (state = 0);
        default:
            return state;
    }
}
export default counterReducer;

Simple Counter Example: components/Counter.js

  • Dispatching Actions
    • When we call store.dispatch(action), the store
      1) runs the reducer,
      2) calculates the updated state, and
      3) runs the subscribers to update the UI.
  • Selectors
    • Selectors are functions that accepts specific pieces of information from a store state value and return data that is based on that state
import {useSelector, useDispatch } from 'react-redux';
const Counter = () => {
    const dispatch = useDispatch();
    const counter = useSelector(state => state);
    const incrementHandler = () => {
        dispatch({type: 'INCREMENT'});
    }
    const decrementHandler = () => {
        dispatch({type: 'DECREMENT'});
    }
    const resetHandler = () => {
        dispatch({type: 'RESET'});
    }
    return (
        <div>
            <h2>Redux Counter</h2>
            <div>{counter}</div>
            <button onClick={incrementHandler}>Increment</button>
            <button onClick={decrementHandler}>Decrement</button>
            <button onClick={resetHandler}>Reset</button>
        </div>
    );
};
export default Counter;

Simple Counter Example: store/index.js & App.js

import {createStore} from "redux";
import counterReducer from "../components/counterReducer";
const store = createStore(counterReducer);
export default store;
import React from 'react';
import Counter from './components/Counter';
function App() {
    return (
        <Counter />
    );
}
export default App;

Simple Counter Example: src/index.js

  • The <Provider> component makes the Redux store available to any nested components that need to access the Redux store.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import store from './store/index';
import {Provider} from "react-redux";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Provider store={store}>
        <App />
    </Provider>
);

Referencing values with Refs

Adding a ref to the components

  • Import the useRef Hook to add a ref
import { useRef } from 'react';
  • Call the useRef Hook and pass the initial value
const ref = useRef(0);
  • useRef returns an object like
{
current: 0 // The value you passed to useRef
}
  • Accessing the current value of the ref through the ref.current

ref is like a secret pocket of a component that React doesn’t track

  • Setting state re-renders a component. Changing a ref does not
import { useRef } from 'react';
export default function Counter() {
    let ref = useRef(0);
    function handleClick() {
        ref.current = ref.current + 1;
        alert('You clicked ' + ref.current + ' times!');
    }
    return (
        <button onClick={handleClick}>
            Click me!
        </button>
    );
}
  • Unlike state, ref is a plain JavaScript object with the current property that you can read and modify.
    ref는 상태와 달리 읽고 수정할 수 있는 현재 속성을 가진 일반 자바스크립트 개체입니다.

useeffect 사용하지 않고도 바로 반영이 가능하구나

0개의 댓글