"this.context" returns an empty object - javascript

I can't figure out why:
I have react _ react-dom 16.8x
I have a component between my Provider and my Consumer to avoid cyclic call between the two aforementioned elements.
I would my context be accessible directly in lifecycle component and seen ReactJS propose the this.context's method to do that.
Here my sandbox
Here my reactjs snippet
import React, {Component}from "react";
import "./App.css";
// first we will make a new context
const MyContext = React.createContext();
// Then create a provider Component
class MyProvider extends Component {
state = {
name: 'Wes',
age: 100,
cool: true
}
render() {
return (
<MyContext.Provider value={{
state: this.state,
growAYearOlder: () => this.setState({
age: this.state.age + 1
})
}}>
{this.props.children}
</MyContext.Provider>
)
}
}
const Family = (props) => (
<div className="family">
<Person />
</div>
)
class Person extends Component {
componentDidMount(){
console.log("context: ", this.context)
}
render() {
console.log("context: ", this.context)
return (
<div className="person">
<MyContext.Consumer>
{(context) => (
<React.Fragment>
<p>Age: {context.state.age}</p>
<p>Name: {context.state.name}</p>
<button onClick={context.growAYearOlder}>🍰🍥🎂</button>
</React.Fragment>
)}
</MyContext.Consumer>
</div>
)
}
}
class App extends Component {
render() {
return (
<MyProvider>
<div>
<p>I am the app</p>
<Family />
</div>
</MyProvider>
);
}
}
export default App;
Why this.context is empty?
Any hint would be great,
thanks

You need to set contextType to consume a context.
On the react website it specifies as much anyway.
So the only change required:
class Person extends Component {
componentDidMount(){
console.log("context: ", this.context)
}
render() {
console.log("context: ", this.context)
return (
<div className="person">
<MyContext.Consumer>
{(context) => (
<React.Fragment>
<p>Age: {context.state.age}</p>
<p>Name: {context.state.name}</p>
<button onClick={context.growAYearOlder}>🍰🍥🎂</button>
</React.Fragment>
)}
</MyContext.Consumer>
</div>
)
}
}
Person.contextType = MyContext;
Hope this helps!

If you need access to MyContext in Person, you can wrap Person in the consumer and pass in context when you render it in Family:
const Family = (props) => (
<div className="family">
<MyContext.Consumer>
{(context) => {
<Person context={context} />
}}
</MyContext.Consumer>
</div>
)
class Person extends Component {
componentDidMount(){
console.log("context: ", this.props.context)
}
render() {
const { context } = this.props;
console.log("context: ", context)
return (
<div className="person">
<React.Fragment>
<p>Age: {context.state.age}</p>
<p>Name: {context.state.name}</p>
<button onClick={context.growAYearOlder}>🍰🍥🎂</button>
</React.Fragment>
</div>
)
}
}

Related

React | ForwardedRef using React.Component

I'm creating a custom component in React, and I need to export it using forwardedRef. But when I try, this error occurs:
error
my code:
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>{
ref?: React.RefObject<HTMLButtonElement>;
}
class Button extends React.Component<ButtonProps> {
render() {
const {
ref,
children,
...otherProps
} = this.props;
return (
<button
{...otherProps}
ref={ref}
>
{children}
</button>
)
}
}
const ButtonForwarded = React.forwardRef<ButtonProps>((props, ref) =>
<Button {...props} ref={ref} /> );
ButtonForwarded.displayName = 'Button';
export default ButtonForwarded;
Create the ButtonForwarded component like this:
const ButtonForwarded = React.forwardRef((props: ButtonProps, ref: LegacyRef<Button>) => <Button {...props} ref={ref} /> );

Something went wrong!Element type is invalid: expected a string (for built-in components) or a class/function For HOC

import React, { useState } from 'react';
import CreateProject from './CreateProjectWithName';
function Enhanced(WrappedComponent) {
// ...and returns another component...
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
render() {
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
const Wrapper = (props) => {
const Comp = Enhanced(<CreateProject />);
return (
<div>
<Comp />
<div>
{/* <button onClick={() => updateView()} /> */}
</div>
</div>
);
};
This code block just won't work. What am I doing wrong? Which principal fundamental of React am I missing out on? What is the correct way of rendering an HOC?
The HOC takes in a component reference what you are providing it is an instance when you use it like
const Comp = Enhanced(<CreateProject />);
The correct way would be
const Comp = Enhanced(CreateProject);
Also for performance reasons and for proper execution of lifecycle you must create a wrapped component with HOC outside of your component
const Comp = Enhanced(CreateProject);
const Wrapper = (props) => {
return (
<div>
<Comp />
<div>
{/* <button onClick={() => updateView()} /> */}
</div>
</div>
);
};
function Enhanced(WrappedComponent) {
// ...and returns another component...
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
render() {
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
const Wrapper = (props) => {
const Comp = Enhanced(CreateProject );
return (
<div>
<Comp />
<div>
{/* <button onClick={() => updateView()} /> */}
</div>
</div>
);
};

How to pass props from child component to parent component to another child component in ReactJS?

I'm currently creating a search bar where App is the parent and Header, Home are the children. I'm trying to pass the input from Header to Home via App but it seems that when I try to load in {this.state.searchValue} it does nothing.
I'm lost of where I am wrong in my code. I'm also using Route to route the props from Header to Home.
Here is my code:
Header.js (Child 1)
class Header extends Component {
constructor(props) {
super(props);
this.state = {
search: "",
};
}
onChange = (event) => {
this.setState({ search: event.target.value });
};
submitSearch = (event) => {
event.preventDefault();
console.log(this.state.search);
this.props.passSearchData(this.state.search);
};
render() {
return (
<React.Fragment>
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<form className="form-inline">
<input
className="form-control mr-sm-2"
type="text"
placeholder="Search"
onChange={this.onChange}
/>
<button
className="btn btn-danger"
type="submit"
onClick={this.submitSearch}
>
Search
</button>
</form>
</nav>
</React.Fragment>
);
}
}
export default Header;
App.js (Parent)
class App extends Component {
constructor() {
super();
this.state = {
searchValue: "",
};
}
handleSearchData = (search) => {
this.setState({ searchValue: search });
};
componentDidMount() {
this.props.getItems();
}
render() {
return (
<div className="App">
<Router>
<Header passSearchData={this.handleSearchData} />
<Route
exact
path="/"
render={(props) => (
<Home {...props} searchValue={this.state.searchValue} />
)}
/>
</Router>
<Footer />
</div>
);
}
}
Home.js
class Catalog extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>
<p>{this.props.searchValue}</p>
</div>
);
}
}
I think using react context better for this problem, because passing state between routes quite painful
First u declare your own Provider to act as intermediary between components.
The context will save all the application state. and to consume at your components, simply use useContext and pass the Context u want to use, at this useCase, i call it AppContext. by using the same context, your components get the same state and trigger update immediately
The solution i provide is using functional component. If u are using class Component, just simply create a functional component, then pass the context to the class component
import React, { useContext, useState } from 'react'
const AppContext = React.createContext({})
const AppProvider = props => {
const [currentState, setCurrentState] = useState(null)
const handleState = value => {
setCurrentState(value)
}
const contextValue = { handleState, currentState }
return (
<AppContext.Provider value={contextValue}>
{props.children}
</AppContext.Provider>
)
}
const Child1 = props => {
const { handleState } = useContext(AppContext)
const handleClick = e => {
handleState(e.target.values)
}
return (
<button onClick={handleClick}>Change State</button>
)
}
const Child2 = props => {
const { currentState } = useContext(AppContext)
return (
<h1>{currentState}</h1>
)
}
const Parent = props => {
return (
<Router>
<AppProvider>
<Route component={Child1} />
<Route component={Child2} />
</AppProvider>
</Router>
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

How can I pass a ref to HOC that uses onClickOutside('react-onclickoutside')?

I use onClickOutside('react-onclickoutside') for my HOC and I can't pass ref for this HOC, I have something like below and an error appears:
const inputRef = useRef();
....
<SomeCompontnt
inputRef={inputRef}
items={items}
onSelect={onSelect}
value={selectedItem}
/>
....
export default onClickOutside(forwardRef(
(props, inputRef) => <MyHoc inputRef={inputRef} {...props} />)
);
....
Errors
Uncaught TypeError: Cannot read property 'isReactComponent' of
undefined
at onClickOutside.render (react-onclickoutside.es.js?7e48:325)
try this:
you can see MyHoc onClick output in OnclickoutsideDemo MyHookClick console
import React from "react";
import { render } from "react-dom";
import onClickOutside from "react-onclickoutside";
class OnclickoutsideDemo extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const options = this.state.showOptions ? (
<MyHookClick
onClick={e => console.log(e)}
handleClickOutside={() => this.setState({ showOptions: false })}
/>
) : null;
return (
<div>
<span
onClick={() =>
this.setState({ showOptions: !this.state.showOptions })
}
>
Click Me
</span>
{options}
</div>
);
}
}
const MyHoc = React.forwardRef((props, ref) => (
<div ref={ref} onClick={e => props.onClick("HaHa")}>
Click Me to see HaHa in console !
</div>
));
const MyHookClick = onClickOutside(props => (
<MyHoc ref={props.inputRef} {...props} />
));
export default OnclickoutsideDemo;
render(<OnclickoutsideDemo />, document.getElementById("root"));

How to get data from react context

I have a React class called GlobalDataProvider:
import React, { Component } from 'react';
const DataContext = React.createContext();
export default DataContext;
export class DataProvider extends Component {
state = {
title: 'Some title'
}
render() {
return (
<DataContext.Provider
value={{state: this.state}}>
{this.props.children}
</DataContext.Provider>
)}
}
And I am trying to use data in another file "PageSection.js" like this:
import React from 'react';
import DataContext from '../data/GlobalDataProvider';
const PageSection= () =>{
return (
<section className="page-section">
<DataContext.Consumer>
{(context) => (
<h1>{ context.state.title }</h1>
)}
</DataContext.Consumer>
</section>
);
};
However this does not work for some reason. I get this error message:
TypeError: Cannot read property 'state' of undefined,
in PageSection.js line 11 (the line with this code: { context.state.title }).
What am I doing wrong?
Do I have to import the class DataProvider? or only the Context variable?
I suspect you need your DataContext.Consumer to be a child element of the DataContext.Provider. Something like this...
import React from 'react';
import DataContext, { DataProvider } from '../data/GlobalDataProvider';
const PageSection= () =>{
return (
<section className="page-section">
<DataProvider>
<DataContext.Consumer>
{(context) => (
<h1>{ context.state.title }</h1>
)}
</DataContext.Consumer>
</DataProvider>
</section>
);
};
try:
export class DataProvider extends Component {
state = {
title: 'Some title'
}
render() {
return (
<DataContext.Provider
value={this.state}>
{this.props.children}
</DataContext.Provider>
)}
}
const PageSection= () =>{
return (
<DataProvider>
<section className="page-section">
<DataContext.Consumer>
{(context) => (
<h1>{ context.title }</h1>
)}
</DataContext.Consumer>
</section>
</DataProvider>
);
};

Categories

Resources