简体   繁体   English

无法访问 React 中的上下文值

[英]Can't access context values in React

I'm trying to pass a value from one component within a context consumer to another component as a prop it says it's undefined .我试图将一个值从上下文使用者中的一个组件传递给另一个组件作为道具,它说它是undefined

<ReadingSessionContext.Consumer>
  {(context) => {
    console.dir(context.state.session.duration) // Value is printed here
    return (
      ...
        <Timer duration={context.state.session.duration} />
      ...
    )
  }}
</ReadingSessionContext.Consumer>

And the Timer componentTimer组件

class Timer extends React.Component {
  state = {
    "minutes": this.props.duration.split(":")[1].parseInt(), // Returns error here
    "seconds": this.props.duration.split(":")[2].parseInt()  // Returns error here
  }

  componentDidMount() {
    console.dir(this.props) // Value is undefined here
    this.myInterval = setInterval(() => {
      const { seconds, minutes } = this.state;

      if (seconds > 0) {
        this.setState(({ seconds }) => ({
          seconds: seconds - 1
        }))
      }

      if (seconds === 0) {
        if (minutes === 0) {
          clearInterval(this.myInterval)
        } else {
          this.setState(({ minutes }) => ({
            minutes: minutes - 1,
            seconds: 59
          }))
        }
      }
    }, 1000)
  }

  render() {
    const { minutes, seconds } = this.state;

    return (
      <Typography component="h1" variant="h5">
        Time Remaining: { minutes }:{ seconds < 10 ? `0${ seconds }` : seconds }
      </Typography>
    )
  }
}

I've also tried to refactor this so that the Timer component consumes the context, rather than passing it as a prop:我还尝试重构它,以便Timer组件使用上下文,而不是将其作为道具传递:

function Timer() {
  const context = useContext(ReadingSessionContext);
  const [minutes, setMinutes] = useState(3);
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    let duration = context.state.session.duration; // Value is accessable here
    console.log(duration); // This logs to the console correctly, and it is a string
    setMinutes(duration.split(":")[1].parseInt()); // Throws error here
    setSeconds(duration.split(":")[2].parseInt()); // Throws error here

    let timerInterval = setInterval(() => {
      if (seconds > 0) {
        setSeconds(seconds - 1);
      }

      if (seconds === 0) {
        if (minutes === 0) {
          clearInterval(this.timerInterval)
        } else {
          setMinutes(minutes - 1);
          setSeconds(59)
        }
      }
    }, 1000);
  });
}

In each case, the error that gets thrown is - Uncaught TypeError: Cannot read property 'split' of undefined在每种情况下,抛出的错误都是 - Uncaught TypeError: Cannot read property 'split' of undefined

However, in every instance where I inspect the value before calling .split() it tells me that the value exists, and is in fact correct, but all of a sudden stops existing as soon as I try to perform some action on the string但是,在调用.split()之前检查值的每个实例中,它告诉我该值存在,并且实际上是正确的,但是一旦我尝试对字符串执行某些操作,它就会突然停止存在

Provider.jsx Provider.jsx

import React from "react";
import axios from "axios";
import ReadingSessionContext from "./Context";

export default class ReadingSessionProvider extends React.Component {

  /**
   * Set the initial state of the `ReadingSessionProvider`
   * @param {*} props 
   */
  state = {
    "translationUrl": process.env.REACT_APP_BACKEND_URL + "translate/",
    "readingSessionUrl": process.env.REACT_APP_BACKEND_URL + "reading-sessions/",
    "session": {},
    "book": {},
    "translations": [],
    "serverPage": 1,
    "clientPage": 0,
    "limit": 10,
    "totalResults": 0,
    "sessionId": 0,
    "headers": {
      "Content-type": "application/json",
      "Authorization": "Token " +  localStorage.getItem("token"),
    }
  }

  /**
   * After the component mounts, call the `getReadingSession` method
   * and update the state with response
   */
  async componentDidMount() {
    let data = await this.getReadingSession();
    this.setState({"session": data.data});
    this.setState({"book": data.data.library_item.book});
    await this.getTranslations()
  }

  /**
   * Call the API and get data for this specific reading session
   */
  async getReadingSession() {
    let result = await axios.get(
      this.state.readingSessionUrl + window.location.href.split('/')[5] + "/",
      {headers: this.state.headers}
    );
    return result;
  }

  makeUrl = sessionId => {
    return `${this.state.translationUrl}?page=${this.state.serverPage}&limit=${this.state.limit}&sessionId=${this.state.session.id}`;
  }

  /**
   * Make the API call to the server to retrieve a list of the translations
   * for the currently logged in user.
   */
  getTranslations = async () => {
    try {
      let url = `${this.state.translationUrl}?page=${this.state.serverPage}&limit=${this.state.limit}&sessionId=${this.state.session.id}`
      let response = await axios.get(url, {headers: this.state.headers});
      await this.setState({"translations": response.data.results});
      await this.setState({"totalResults": response.data.count});
    } catch (error) {
      console.log(error);
    }
  }

  /**
   * Submit the text that the user has input and get the updated 
   * list of translations from the API
   */
  submitText = async (e, text) => {
    console.log("hello?")
    let data = {
      "text_to_be_translated": text,
      "session": this.state.session.id
    };

    try {
      await axios.post(this.state.translationUrl, data, {headers: this.state.headers});
      let paginationUrl = `${this.state.translationUrl}?page=${this.state.serverPage}&limit=${this.state.limit}&sessionId=${this.state.session.id}`;
      this.getTranslations(paginationUrl);
    } catch (error) {
      console.dir(error);
    }
  }

  setSessionId = sessionId => {
    this.setState({"sessionId": sessionId});
    console.log("called")
  }

  handleChangePage = async (event, newPage) => {
    this.setState({"serverPage": newPage + 1})
    this.setState({"clientPage": newPage})
    let url = await `${this.state.translationUrl}translate/?page=${newPage + 1}&limit=${this.state.limit}&sessionId=${this.state.session.id}`
    console.log(url)
    await this.getTranslations(url);
  }

  render() {
    return (
      <ReadingSessionContext.Provider value={{
        state: this.state,
        getTranslations: this.getTranslations,
        submitText: this.submitText,
        handleChangePage: this.handleChangePage,
        setSessionId: this.setSessionId,
        makeUrl: this.makeUrl

      }}>
        {this.props.children}
      </ReadingSessionContext.Provider>
    )
  }
}

Context.jsx上下文.jsx

import React from "react";

const ReadingSessionContext = React.createContext();

export default ReadingSessionContext;

Okay.好的。 You're really close.你真的很亲近。 You still need to wrap your root component or hierachy with ReadingSessionProvider.您仍然需要使用 ReadingSessionProvider 包装您的根组件或层次结构。 So this is how it works generally.所以这就是它的一般工作方式。

  1. You create the context with const ReadingSessionContext = React.createContext();你用const ReadingSessionContext = React.createContext();创建上下文const ReadingSessionContext = React.createContext();
  2. You use it to make a context provider.您可以使用它来创建上下文提供程序。 Which you have done when you created the the ReadingSessionProvider class.您在创建ReadingSessionProvider类时所做的工作。
  3. You use the ReadingSessionProvider wrap the app section you want to access the consumer.您使用ReadingSessionProvider包装要访问使用者的应用程序部分。 So say所以说
<ReadingSessionProvider>
  <App />
</ReadingSessionProvider>
  1. Now the children of App component can use the values set in the ReadingSessionProvider.现在 App 组件的子组件可以使用在 ReadingSessionProvider 中设置的值。 Either with useContext(ReadingSessionContext) or with ReadingSessionContext.Consumer使用useContext(ReadingSessionContext)或使用ReadingSessionContext.Consumer

See this video from Wes Bos for more information: https://youtu.be/XLJN4JfniH4有关更多信息,请参阅 Wes Bos 的此视频: https : //youtu.be/XLJN4JfniH4

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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