React export React.createContext not defined - reactjs

I am trying to learn React Context and got stuck. Need help.
App.js
import React from 'react';
import Header from './components/Header';
export const MyContext = React.createContext("Default");
class App extends React.Component {
render() {
return (
<MyContext.Provider value="dark">
<Header />
</MyContext.Provider>
);
}
}
export default App;
Header/index.js
import React, { Component } from 'react'
import { MyContext } from "./../../App";
class Header extends Component {
//static contextType = MyContext;
render() {
return (
<div>
{this.context}
</div>
)
}
}
Header.contextType = MyContext;
export default Header;
Got an error MyContext is not defined.
It works when i move Header class to App.js
What am i doing wrong? Tnx for your help

There are two ways to use context either use:
1. By using context consumer :
<MyContext.Consumer>
{
contextValue => {
return <div>
{value}
</div>
}
}
<MyContext.Consumer>
2. By assigning context to a object:
static contextType = MyContext;
render(){
const {value1,value2.......} = this.context
}
For more information about Context visit the React official page.
https://reactjs.org/docs/context.html

The provider only holds the the value for you(a bit like a store). It is the consumer that makes it available to your components.
Headerjs should look like this
// Header.js
import React, { Component } from 'react'
import { MyContext } from "./../../App";
class Header extends Component {
//static contextType = MyContext;
render() {
return (
<MyContext.Consumer>
{ value => {
return <div>
{value}
</div>
}}
<MyContext.Consumer>
)
}
}
// Header.contextType = MyContext; not needed for react v16+
export default Header;
To get more power out of Context i will suggest combining with Higher Order Components. for example if what you want is a theming system
you can do this.
import React from "react";
const themes = {
dark: {
background: "#333"
},
light: {
background: "#f5f5f9"
}
};
const { Provider, Consumer } = React.createContext(themes);
export const ThemeProvider = ({ children }) => {
return <Provider value={themes}>{children}</Provider>
};
export const withTheme = theme => {
return Component => props => <Consumer>
{themes => {
return <Component {...props} style={{ ...themes[theme]}} />;
}}
</Consumer>
};
in app.js
import Header from "./Header";
import { ThemeProvider } from './Theme'
class App extends React.Component {
render() {
return (
<ThemeProvider>
<Header />
</ThemeProvider>
);
}
}
and lastly Header.js
import React, { Component } from "react";
import { withTheme } from "./Theme";
class Header extends Component {
//static contextType = MyContext;
render() {
return <h1 style={{ ...this.props.style }}>Header</h1>;
}
}
export default withTheme("dark")(Header);
You can read MY article on using context for auth for more

Related

Why component with Context Provider doesn't re-render

I was looking for the answer why react component with Context Provider doesn't re-render but i couldn't find answer proper for me to understand why.
Moreover i want to mention Im using GatsbyJS.
Here's App.context.js:
const defaultValue = {
menu: false,
handleMenu: () => { },
}
const AppContext = createContext(defaultValue);
export default AppContext;
export { defaultValue };
Next, down below there's Provider element App.provider.js:
import AppContext, { defaultValue } from './App.context';
class AppProvider extends Component {
constructor(props) {
super(props);
this.state = defaultValue
}
handleMenu = () => {
if (this.state.menu) {
this.setState({
menu: false
})
} else {
this.setState({
menu: true
})
}
}
render() {
return (
<AppContext.Provider value={{
menu: this.state.menu,
handleMenu: this.handleMenu,
}}>
{this.props.children}
</AppContext.Provider>
);
}
}
export default AppProvider;
Then, I'm using this provider at the beginning of elements tree:
//Components
import Header from '../components/header';
import Footer from '../components/footer';
import MainWrap from '../components/mainWrap';
//Context
import AppProvider from '../context/App.provider';
const Layout = ({ children }) => {
return (
<AppProvider>
<MainWrap>
<Header />
{children}
<Footer />
</MainWrap>
</AppProvider>
);
}
export default Layout;
Here's MainWrap component:
//Styles
import wrapStyles from '../styles/wrapper.module.scss';
//Context
import AppContext from '../context/App.context';
const MainWrap = ({children}) => {
const {menu} = useContext(AppContext);
return (
<div className={menu?wrapStyles.wrap:wrapStyles.wrapActive}>{children}</div>
);
}
export default MainWrap;
When context value change, child components like MainPage re-render properly, but why component with Provider does not, so i can't instead of using next wrap component (MainPage) just put a div in component with Provider:
//Components
import Header from '../components/header';
import Footer from '../components/footer';
//Styles
import wrapStyles from '../styles/wrapper.module.scss';
//Context
import AppProvider from '../context/App.provider';
import AppContext from '../context/App.context';
const Layout = ({ children }) => {
const {menu} = useContext(AppContext);
return (
<AppProvider>
<div className={menu?wrapStyles.wrap:wrapStyles.wrapActive}>
<Header />
{children}
<Footer />
</div>
</AppProvider>
);
}
export default Layout;
I hope it will be understandable.

How can I call hook calls from react class bases components?

I want to use functions like useMediaQuery in class based components.
How can I make achieve this ?
import React from 'react';
import clsx from 'clsx';
import { withStyles ,withTheme ,withMediaQuery } from '#material-ui/styles';
import { useMediaQuery } from '#material-ui/core'
class Main extends React.Component{
render(){
const { children ,classes,theme} = this.props;
const isDesktop = useMediaQuery(theme.breakpoints.up('lg'),{
defaultMatches :true
});
return (
<div className ={clsx({
[classes.root] : true,
[classes.shiftContent] : isDesktop
})}>
<main>
{children}
</main>
</div>
);
}
}
You can wrap your useMediaQuery and the rest of the logic in another component:
const WithClasses = ({ children, theme, classes }) => {
const isDesktop = useMediaQuery(theme.breakpoints.up("lg"), {
defaultMatches: true
});
return (
<div
className={clsx({
[classes.root]: true,
[classes.shiftContent]: isDesktop
})}
>
{children}
</div>
);
};
Then use it in your layout like this:
class Main extends React.Component {
render() {
const { children, classes, theme } = this.props;
return (
<WithClasses theme={theme} classes={classes}>
<main>{children}</main>
</WithClasses>
);
}
}

Programatically going back to home page with React

I've read the articles on here on how to do this and have chosen the withRouter(({ history }) => history.push("/")); method, but my code below isn't working.. What am I doing wrong?
import React from "react";
import SearchBox from "./SearchBox";
import { withRouter } from "react-router-dom";
class SearchParams extends React.Component {
handleSearchSubmit() {
withRouter(({ history }) => history.push("/"));
}
render() {
return (
<div className="search-route">
<SearchBox search={this.handleSearchSubmit} />
</div>
);
}
}
export default SearchParams;
withRouter is a higher order component which takes a component as first argument and will make it so that component gets the history added to its regular props.
You can instead use it on the component when you export it, and access the history from this.props.history.
class SearchParams extends React.Component {
handleSearchSubmit = () => {
this.props.history.push("/");
};
render() {
return (
<div className="search-route">
<SearchBox search={this.handleSearchSubmit} />
</div>
);
}
}
export default withRouter(SearchParams);

React Context and Next JS

I'm trying to add simple React Context to my app. I create Context in "./components/DataProvider.js" that looks like this:
import React, { Component } from 'react'
const DataContext = React.createContext()
class DataProvider extends Component {
state = {
isAddButtonClicked: false
}
changeAddButtonState = () => {
if( this.state.isAddButtonClicked ) {
this.setState({
isAddButtonClicked: false
})
} else {
this.setState({
isAddButtonClicked: true
})
}
}
render() {
return(
<DataContext.Provider
value={{
isAddButtonClicked: this.state.isAddButtonClicked,
changeAddButtonState: () => {
if( this.state.isAddButtonClicked ) {
this.setState({
isAddButtonClicked: false
})
} else {
this.setState({
isAddButtonClicked: true
})
}
}
}}
>
{this.props.children}
</DataContext.Provider>
)
}
}
const DataConsumer = DataContext.Consumer
export default DataProvider
export { DataConsumer }
Which then I added to "./pages/_app.js"
import App, { Container } from 'next/app'
import DataProvider from '../components/DataProvider'
class MyApp extends App {
render () {
const { Component, pageProps } = this.props
return (
<Container>
<DataProvider>
<Component {...pageProps} />
</DataProvider>
</Container>
)
}
}
export default MyApp
And consume it in "./components/AddPostButton.js".
import React, {Component} from 'react'
import { DataConsumer } from './DataProvider'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faPlus } from '#fortawesome/free-solid-svg-icons'
class AddPostButton extends Component {
render() {
return (
<div>
<DataConsumer>
{({ changeAddButtonState }) => (
<a onClick={changeAddButtonState}>
<FontAwesomeIcon icon={faPlus} color='#fff' />
</a>
)}
</DataConsumer>
</div>
)
}
}
export default AddPostButton
But I get this error "Cannot read property 'changeAddButtonState' of undefined". I'm using React 16.7 and NextJS 7.0.2. Don't know what is wrong.
The second question is should I use one Context for everything or just use them as Model in MVC pattern?
I fixed it by moving changeAddButtonState to Context Component state so my DataProvider.js now looks like this
import React, { Component } from 'react'
const DataContext = React.createContext()
class DataProvider extends Component {
state = {
isAddButtonClicked: false,
changeAddButtonState: () => {
if (this.state.isAddButtonClicked) {
this.setState({
isAddButtonClicked: false
})
} else {
this.setState({
isAddButtonClicked: true
})
}
}
}
render() {
return(
<DataContext.Provider
value={this.state}
>
{this.props.children}
</DataContext.Provider>
)
}
}
const DataConsumer = DataContext.Consumer
export default DataProvider
export { DataConsumer }
And then in AddButton component I changed code to look like this
import React, {Component} from 'react'
import { DataConsumer } from './DataProvider'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faPlus } from '#fortawesome/free-solid-svg-icons'
class AddPostButton extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
<DataConsumer>
{(context) => (
<a onClick={context.changeAddButtonState}>
<FontAwesomeIcon icon={faPlus} color='#fff' />
</a>
)}
</DataConsumer>
</div>
)
}
}
export default AddPostButton

Passing redux action to another component through TabNavigator

I have just finish following this tutorial https://www.youtube.com/watch?v=3msLwu25SQY&list=PLk083BmAphjtGWyZUuo1BiCS_ZAgps6j5
Now I want to implement the TabNavigator which look like this
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { ActionCreators } from '../actions'
import { TabNavigator } from 'react-navigation'
import Home from './Home'
const Tabs = TabNavigator({
Home: {
screen: Home,
}
})
class AppContainer extends Component {
render(){
return <Tabs {...this.props}/>
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(ActionCreators, dispatch)
}
export default connect((state) => { return {} }, mapDispatchToProps)(AppContainer)
However the redux action does not pass to the Home component and I got an error saying this.props.[actionname] is not a function.
show you my project
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<Navigator
initialRoute={{component: AppTabs}}
renderScene={(route, navigator) =>
<route.component {...route.args} navigator={navigator} />
}
/>
</Provider>
)
}
}
change the AppTabs to your Tabs, it maybe works.

Resources