2

I want to get some props made in the root layer of my react app:

import React from 'react'
import App, { Container } from 'next/app'

export default class MyApp extends App {
  static async getInitialProps({ Component, router, ctx }) {
    let pageProps = {}

    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx)
    }

    return { pageProps }
  }

  state = {
    language: "pl"
  };

  render () {
    const { Component, pageProps } = this.props

    return (
      <Container>
        <Component lang={this.state.language} />
      </Container>
    )
  }
}

so every new React.Component created should inherit those props. But I'm not sure how I can get them. Let's say I have another component which is <Nav/>.

Shouldn't I be able to get it via props.lang inside Nav. When I try it says lang undefined.

supersize
  • 13,764
  • 18
  • 74
  • 133
  • Did you try `props.lang`, or, `this.props.lang`? – HoldOffHunger Oct 02 '18 at 21:30
  • I tried, it is undefined. Notice that I try to access it within another component like: `const Nav = () => (
    {this.props.lang}
    )`, I was thinking that Nav inherits from React.Component it should get `lang`?
    – supersize Oct 02 '18 at 21:35
  • I see a state definition. You need to call `this.setState(state);`, if you want this.state.language to be what you think it is. – HoldOffHunger Oct 02 '18 at 21:37
  • @HoldOffHunger only when I need update right? – supersize Oct 02 '18 at 21:39
  • Hrm, just took a quick peek at my own code -- the initial state is set in `constructor(props)`, and is defined like `super(); this.state = (somestate);`. – HoldOffHunger Oct 02 '18 at 21:41
  • I'm heading out now -- I don't have any posts precisely like this one, but if you are interested in sharing state and/or functions between components, I have a detailed answer on it here: https://stackoverflow.com/a/51661103/2430549 – HoldOffHunger Oct 02 '18 at 21:47

4 Answers4

0

I'm seeing a couple problems in your code example.

First, props are a property on your component, they should be accessed via this.props.

Here is a basic example of passing props to a child component:

import React, { Component } from 'react';

class App extends Component {
  render() {
    const greeting = 'Welcome to React';

    return (
      <div>
        <Greeting greeting={greeting} />
      </div>
    );
  }
}

class Greeting extends Component {
  render() {
    return <h1>{this.props.greeting}</h1>;
  }
}

export default App;

Using the code sample above, it would seem that your mistake was to use return <h1>{props.greeting}</h1>; instead of return <h1>{this.props.greeting}</h1>;

Second, it would appear that your component setup is a little off. I would expect your component declaration to look something like this:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

In your code sample, there's no constructor function and state doesn't appear to be set as a property of your component.

Elliot B.
  • 17,060
  • 10
  • 80
  • 101
  • I don't understand, I would need to pass the props along through all elements down? I thought if I set props on the Component I have them available throughout all elements inheriting from Component without passing it to each component. – supersize Oct 02 '18 at 21:44
  • Correct, you would have to pass the `props` to each child component. – Elliot B. Oct 02 '18 at 21:45
  • So assuming I have an `index.js` rendering a `Nav` and a `Content` component I just do ` `? – supersize Oct 02 '18 at 21:49
  • Doesn't worry I'm afraid. – supersize Oct 03 '18 at 09:04
0

Inside of the example <Nav/> component, you must specify at least one argument in the component's function if you wish to access this.props. For example:

const Nav = (props) => ( <div> {this.props.lang} </div> )

Hope this helps!

0

Summary of my comments above:

Did you try props.lang, or, this.props.lang?

Because you need this.props.lang to access the property.

Hrm, just took a quick peek at my own code -- the initial state is set in constructor(props), and is defined like super(); this.state = (somestate);.

Because you need to set the state in the constructor of classes.

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
0

I would suggest moving language to the React Context API

So this way you create a context

// context.js
import React from 'react';

export const LangContext = React.createContext('pl');

and provide it inside _app.js

// app.js
import React from 'react';
import App, { Container } from 'next/app';
import { LangContext } from '../context';

export default class MyApp extends App {
  static async getInitialProps({ Component, router, ctx }) {
    let pageProps = {};

    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx);
    }

    return { pageProps };
  }

  state = {
    language: 'EN'
  };

  render() {
    const { Component, pageProps } = this.props;

    return (
      <Container>
        <LangContext.Provider value={this.state.language}>
          <Component {...pageProps} />
        </LangContext.Provider>
      </Container>
    );
  }
}

and whenever you need to access language value you dont need to pass it anymore. It will be available on LangContext. Example usage

// Nav.js
import Link from 'next/link';
import { LangContext } from '../context';

function Nav() {
  return (
    <LangContext.Consumer>
      {lang => {
        return (
          <div className="site-nav">
            <Link href="/">
              <a>index</a>
            </Link>
            <Link href="/about">
              <a>about</a>
            </Link>
            language = {lang}
          </div>
        );
      }}
    </LangContext.Consumer>
  );
}

export default Nav;

This helps to solve the issue of passing lang props to pages and then to some specific components like Nav. Just wrap a component into a <LangContext.Consumer> if you need it.

Example index.js page

// index.js
import Nav from '../components/Nav';

export default () => (
  <div>
    <Nav />
    <hr />
    Welcome to index.js!
  </div>
);

** One note: as far as I see you can only use <SomeContext.Provider> inside _app.js

iurii
  • 4,142
  • 2
  • 23
  • 28
  • Seems like a great idea but doesn't seem to work for me. I replicated your setup and `lang` is not defined in Nav. – supersize Oct 03 '18 at 09:11
  • I setup a quick repo with the code https://github.com/yuyokk/nextjs-context-api-usage-example/ – iurii Oct 03 '18 at 09:22
  • You are awesome my friend. Thanks! – supersize Oct 03 '18 at 09:29
  • Just one more thing to confirm, this doesn't contradict with props passed to Nav right? So they're available as usual. – supersize Oct 03 '18 at 09:36
  • yes. `` will be available here `function Nav(props) { console.log(props.active) }` – iurii Oct 03 '18 at 09:39
  • see https://reactjs.org/docs/context.html#examples themed-button.js example – iurii Oct 03 '18 at 09:41
  • One last question. How can I set the `lang` as a state within a nested component? I mean so that `LangContext` has a new value. – supersize Oct 03 '18 at 10:30
  • 1
    that's why I added [changeLang](https://github.com/yuyokk/nextjs-context-api-usage-example/blob/master/context.js#L5) method to the context. So now Nav get [two params](https://github.com/yuyokk/nextjs-context-api-usage-example/blob/master/components/Nav.js#L7) where the second one is a function to change the lang value in the context – iurii Oct 03 '18 at 11:21