简体   繁体   中英

React pass "this" to context provider value

I was playing around with useContext in a class component. I've created a simple react (nextjs) application with one button that call a function in the context that update the state and therefore re-render the home component.

import { useContext } from "react";
import { UserContext } from "../provider/TestProvider";

export default function Home() {
    let [contex] = useContext(UserContext);

    return (
        <>
            <button onClick={() => contex.addOne()}>Add 1</button>
            {contex.state.count.map((e) => (
                <div key={Math.random()}> {e} </div>
            ))}
        </>
    );
}
import { createContext, Component } from "react";

export const UserContext = createContext();

export default class TestProvider extends Component {
    state = {count: []};

    constructor(props) {
        super(props);
        this.props = props;
    }

    addOne() {
        let oldState = [...this.state.count];
        oldState.push("test");
        this.setState({ count: oldState });
    }

    render() {
        return (
            <UserContext.Provider value={[this]}>
                {this.props.children}
            </UserContext.Provider>
        );
    }
}

Note: The provider it's wrapped to the entire application, inside _app.js

this is working fine, however I was wondering why I'm forced to pass this inside an array ( value={[this]} )? When I tried to pass only this ( value={this} ) [and modify the context let contex = useContext(UserContext) ], the Home function doesn't react anymore on the change of the state, and therefore no rerender.

From my knowledge of programming both options should behave exactly the same.

What is happening here?

Issue

You are only "forced" to pass the context value as value={[this]} because that is how you are accessing it in the consumer.

let [contex] = useContext(UserContext);

Here you are using array destructuring assignment to get the context value out of the array and accessible.

contex.state.count

In other words, this.state.count and contex.state.count are the same thing.

It rather more that you are forced to consume it from an array because that is how it was provided: value={[this]} .

Solution

Instead of passing the entire this object of the root parent class component it would be better to pass only what you need to, like only the count state and the addOne callback function.

Example:

export const UserContext = createContext({
  addOne: () => {},
  count: [],
});

export default class TestProvider extends Component {
  state = { count: [] };

  addOne = () => {
    this.setState(prevState => ({
      count: [...prevState.count, "test"];
    }));
  }

  render() {
    const { children } = this.props;
    const { count } = this.state;
    return (
      <UserContext.Provider value={{ addOne, count }}> // provided as object
        {children}
      </UserContext.Provider>
    );
  }
}

...

import { useContext } from "react";
import { UserContext } from "../provider/TestProvider";

export default function Home() {
  const { addOne, count } = useContext(UserContext); // object destructuring assignment

  return (
    <>
      <button onClick={addOne}>Add 1</button>
      {count.map((e) => (
        <div key={e}>{e}</div>
      ))}
    </>
  );
}

The main takeaway here though should be that however you are consuming the context value should match how you are providing it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM