Skip to content

Feature Request: Selectively Retrieve Values from Context and update components accordingly #17777

Closed
@GasimGasimzada

Description

@GasimGasimzada

Do you want to request a feature or report a bug?

Feature

What is the current behavior?

Currently, any changes in the context will update all components.

What is the expected behavior?

Currently, when subscribing to context, if any value in the context changes, all consumers will be updated:

const value = useContext(MyContext);

My suggestion is the following: optionally, allow receiving specific values from context values and only update the components if the returned values change. Here is an example:

const data = useContext(MyContext, value => value.data); // value = context value

When the second function argument is defined, only the returned value from the function will be compared and accessed. This will simplify a lot of workflows where multiple contexts are used for multiple values in order to reduce the number of context updates. Here is an example from Hooks FAQ (modified a little bit):

function TodosApp() {
  // Note: `dispatch` won't change between re-renders
  const [todos, dispatch] = useReducer(todosReducer);

  return (
    <TodosData.Provider value={todos}>
      <TodosDispatch.Provider value={dispatch}>
        <DeepTree todos={todos} />
      </TodosDispatch.Provider>
    </TodosData.Provider>
  );
}

function DeepChild(props) {
  // If we want to perform an action, we can get dispatch from context.
  const dispatch = useContext(TodosDispatch);

  function handleClick() {
    dispatch({ type: 'add', text: 'hello' });
  }

  return (
    <button onClick={handleClick}>Add todo</button>
  );
}

function AnotherDeepChild(props) {
  const data = useContext(TodosData);
  ...
}

With the proposed API addition, we can just use the same context to do retrieve two different values:

function TodosApp() {
  // Note: `dispatch` won't change between re-renders
  const [todos, dispatch] = useReducer(todosReducer);

  return (
    <TodosContext.Provider value={{ todos, dispatch }}>
        <DeepTree todos={todos} />
    </TodosContext.Provider>
  );
}

function DeepChild(props) {
  const dispatch = useContext(TodosContext, value => value.dispatch);
}

function AnotherDeepChild(props) {
  const todos = useContext(TodosContext, value => value.todos);
}

Another useful scenario for this addition is dynamically selecting items from a centralized store based on context. If a developer needs to selectively retrieve specific values from an object, they can do it very easily. Something similar to Redux' useSelector but is part of a normal Context Flow:

function UserInfoApp() {
  // Note: `dispatch` won't change between re-renders
  const [user, dispatch] = useReducer(userReducer, { name: ..., dob: ..., active: ... });

  return (
    <UserInfoContext.Provider value={user}>
        <DeepTree todos={todos} />
    </UserInfoContext.Provider>
  );
}

function UserInfoTable(props) {
  const name = useContext(UserInfoContext, value => value.name);
  const dob = useContext(UserInfoContext, value => value.dob);
}

function UserActiveTracker(props) {
  const todos = useContext(UserInfoContext, value => value.active);
}

Now, if active value is changed, only UserActiveTracker will be updated. If name or dob is changed, only UserInfoTable will be activated.

The API can also be implemented for Context.Consumer component:

<MyContext.Consumer selector={value => value.active}> ... </MyContext.Consumer>

and static contextType static class variable:

class MyComponent extends React.Component {
  static contextType = MyContext;
  static contextSelector = value => value.active;

  render() {
    const val = this.context.active;
    const wrongVal = this.context.name; // = undefined

    return ...
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions