1. ホーム
  2. javascript

[解決済み] Context APIを使用しようとすると「renderは関数ではありません」と表示される

2022-02-28 16:58:14

質問

私はContext APIを学ぼうとしています。私が実現したいことは、ログインしたユーザーをヘッダーに表示し、ログイン状態に基づいてメニューオプションを操作することです(stateに'isAuthenticated'を保存することは安全ですか)。

私のコンテキストクラス。

import React from 'react';

const Context = React.createContext();

export class Provider extends React.Component {

    state = {
        isAuthenticated: true,
        user: {
            name: "Joe Smith",
            email: "[email protected]"
        }
    }

    render() {
        return (
            <Context.Provider value={this.state}>
                {this.props.children}
            </Context.Provider>
        )
    }
}

export const Consumer = Context.Consumer;

というわけで、極めて基本的なものです。状態を設定し、後の子コンポーネントでチャップスの名前を表示したいと思います。

私のApp.jsは、すべてのコンポーネントがこのデータにアクセスできるように、'Provider'を使用しています。

import React from 'react';
import { HashRouter , Route, Switch } from 'react-router-dom';
import Home from './components/home';
import Header from './components/header';
import Accounts from './components/accounts';
import Reports from './components/reports';
import Login from './components/login';
import {Container} from 'reactstrap';

import { Provider } from './context';

function App() {
  return (
    <div className="App">
      <Provider>
        <HashRouter>
          <Header />
          <Container>
            <Switch>
              <Route exact path="/" component={Home} />
              <Route exact path="/accounts" component={Accounts} />
              <Route exact path="/reports" component={Reports} />
              <Route exact path="/login" component={Login} />
            </Switch>
          </Container>
        </HashRouter>
      </Provider>
    </div>
  );
}

export default App;

この場合、'Header'は私のコンテキストにアクセスする必要があるわけです。 Headerにはメニューが表示されます(コンテキストに追加した情報に基づいて、オプションやログインボタンなどの表示/非表示を切り替えます)。

メニューの下には、小さな情報バーを表示したい。例えば、ログインしているユーザー名などです。そこで、私のヘッダー・クラスは次のようになります。

import React from 'react';
import Menu from './menu';
import InfoBar from './infobar';
import { Consumer } from '../context';

class Header extends React.Component {

    render() {
        const menuStyle = {
            paddingBottom: "5px"
        }

        return(
            <Consumer>
                <div style={menuStyle}>
                    {value => {
                        console.log(value);
                        return (
                            <h1>Test</h1>
                        )
                    }}
                    <Menu  />
                    <InfoBar />
                </div>
            </Consumer>
        )
    }
}

export default Header;

しかし、今度は問題が起こります。私のコードを実行すると、コンパイルして実行されるのですが、すぐにランタイムエラーが発生するのです。

TypeError: render is not a function updateContextConsumer C:/Storage/Scratch/ReactApp/accufinance/node_modules/react-dom/cjs/react-dom.development.js:16082

リターンと複数の子について何かで読みましたが、私のコードにはその問題がないようです。この問題を理解し、どこで問題が起きているのかを理解するための支援があれば、とても助かります。header'のコードをコメントアウトすると、エラーは出ませんが......画面も出ません。この辺りで発生しているようです。

解決方法は?

Context Consumer はレンダープロップ、具体的には関数を子コンポーネントとして使用するため、その直下の子コンポーネントが(コンポーネントではなく)関数であることが期待されます。あなたの場合、div を関数の中に移動させればよいのです。

<Consumer>
  {value => {
    console.log(value);
    return (
      <div style={menuStyle}>
        <h1>Test</h1>
        <Menu />
        <InfoBar />     
      </div>
    )
  }}
</Consumer>

Render Props は、コンポーネントの内部状態を子コンポーネントに公開したいが、異なるタイプの子コンポーネントでも使用したい場合に、非常に強力です。

このようなパターンです。

class Parent extends Component {
  state = { name: 'Mike' }

  handleChange = (e) => this.setState({ name: e.target.value })

  render() {
    // Here's the main difference: We expect `children` to be
    // a function, and we pass name in as the argument
    return children(state.name);
  }
}

const InputChild = props => <input value={props.name} />

const HeaderChild = props => <h1>{props.name}</h1>

const App = () => {
  return (
    <Parent>
      {name => {
        // We could easily swap in `HeaderChild` here (or
        // anything else!), passing `Parent`'s internal
        // state.name to it instead:
        <InputChild name={name} />
      }
    </Parent>
  )
}

これはContextを機能させるもので、Consumerは子コンポーネントに関する知識を一切持っていませんが、その状態(Providerからの値)を公開することができます。

Reactのドキュメントには、レンダー・プロップスに関する素晴らしいセクションがあります。 https://reactjs.org/docs/render-props.html