Monday, June 3, 2024
 Popular · Latest · Hot · Upcoming
105
rated 0 times [  108] [ 3]  / answers: 1 / hits: 25293  / 5 Years ago, thu, june 13, 2019, 12:00:00

I'm trying to learn the Context API, and what I want to achieve, is showing the Logged In user in my header, as well as manipulate my menu options based on the logged in state (Is it safe to store 'isAuthenticated' in state?)



My context class:



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;


So, extremely basic. Sets up a state, and in some child components later, I want to display the chaps name.



My App.js is the using 'Provider', so that all my components have access to this data:



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;


So in this case, 'Header' needs access to my context.
My Header will show a menu (which, based on some info I'll add to the context, will show or hide options, login buttons etc).



Under the menu, I want to show a small info bar. Logged in user name, for example. So, my header class looks like this:



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;


But the problem now happens. When I run my code, it compiles and runs, but right away, I get a runtime error:




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




I read something about returns and multiple children, but my code seems to not have that issue. Any assistance with understanding the issue and where the problem is happening would be great. If I comment out the code in 'header', no error... but... no screen either. It seems to be occuring in the area.


More From » reactjs

 Answers
7

The Context Consumer uses a render prop, specifically a function as a child component, so it expects its immediate child to be a function (not a component). In your case, you can just move the div inside the function:



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


Render props are really powerful for when you want to expose a component's internal state to its children, but when you also want to use it with different types of children.



The pattern is like this:



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>
)
}


This is what makes Context work, because the Consumer doesn't have any knowledge of the components in its children, but it can expose its state (which is the value from the Provider).



The React docs have a great section on render props: https://reactjs.org/docs/render-props.html


[#52002] Friday, June 7, 2019, 5 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
trevionbronsonr

Total Points: 160
Total Questions: 85
Total Answers: 110

Location: Bonaire
Member since Wed, Mar 29, 2023
1 Year ago
trevionbronsonr questions
;