this.context is always empty in React, contextApi - javascript

test.js
export default class TestScreen extends React.Component {
static contextType= AppProvider;
componentDidMount() {
console.log('test',this.context);
}
render() {
return (
<AppConsumer>
{ (context) => (
<p>{context.favoriteAnimal}</p>
)}
</AppConsumer>
)
}
}
store.js
const initialState = {
favoriteAnimal: "cow",
};
export const AppContext = React.createContext();
export const AppConsumer = AppContext.Consumer;
export class AppProvider extends React.Component {
constructor(props) {
super(props);
this.state = initialState;
}
render() {
return (
<AppContext.Provider value={{
favoriteAnimal: this.state.favoriteAnimal,
}}>
{this.props.children}
</AppContext.Provider>
);
}
}
dependencies: {
"react": "^16.8.4",
"react-dom": "^16.8.4",
"react-scripts": "2.1.8"
},
this.context is empty {},
in test.js. cant find a way out , any help will be appreciated. thank you

Problem is that you are assigning the ContextType to AppProvider which is your component instead of the context returned by React.createContext. Once you make this change, you don't even need to use AppConsumer inside of render method
export default class TestScreen extends React.Component {
static contextType= AppContext;
componentDidMount() {
console.log('test',this.context);
}
render() {
return (
<p>{this.context.favoriteAnimal}</p>
)
}
}

Related

React exporting multiple method calling another method error

Currently i am exporting only one funtin like this and it works great
import React from "react";
import SocialLogin from "from somewwhere";
class GoogleButton extends React.Component {
render() {
const { fontClass, triggerLogin, ...props } = this.props;
return (
<div className="">
Google
</div>
);
}
}
export default SocialLogin(GoogleButton);
But when I try to export multiple functions, it doesn't work.
import React from "react";
import SocialLogin from "from somewhere";
class GoogleButton extends React.Component {
render() {
const { fontClass, triggerLogin, ...props } = this.props;
return (
<div className="">
Google
</div>
);
}
}
class FacebookButton extends React.Component {
render() {
const { fontClass, triggerLogin, ...props } = this.props;
return (
<div className="">
Facebook
</div>
);
}
}
export {
SocialLogin(GoogleButton),
SocialLogin(FacebookButton);
}
Can anyone tell me why it doesnt work? It works when i do like this
export {
SomeFunc,
AnotherFun,
}
But what's wrong with it if i put it inside a functin? Can anyone tell me how can i do it?
You cam simply do this.
import React from "react";
import SocialLogin from "from somewhere";
export class GoogleButton extends React.Component {
render() {
const { fontClass, triggerLogin, ...props } = this.props;
return (
<div className="">
Google
</div>
);
}
}
export class FacebookButton extends React.Component {
render() {
const { fontClass, triggerLogin, ...props } = this.props;
return (
<div className="">
Facebook
</div>
);
}
}
Or you can do this.
.... Existing components
export default {
SocialLoginGoogle: SocialLogin(GoogleButton),
SocialLoginFacebook: SocialLogin(FacebookButton)
}
The below one works as it is considering object's key and value has the same name. Hence, shorthand.
export {
SomeFunc,
AnotherFun,
}

Maintaining a List of React Components then rendering into parent

I have a hypothetical question about maintaining an array of polymorphic React components. Is it possible/good React practice to maintain an array of components that are descendants of a common component, then render them in the container? For instance:
import * as React from 'react';
import GenericBlock from './GenericBlock';
import { BlockTypeA, BlockTypeB, BlockTypeC } from './MyBlocks';
export default class BlockHolder extends React.Component {
blocks : GenericBlock[] = [ <BlockTypeA />, <BlockTypeB />, <BlockTypeC /> ];
render() {
return (
<div id="workspace">
{
this.blocks
}
</div>);
};
};
GenericBlock.tsx
import * as React from 'react';
export default class GenericBlock extends React.Component {
render() { return (<div></div>); }
}
MyBlocks.tsx:
import * as React from 'react';
import GenericBlock from './GenericBlock';
class BlockTypeA extends GenericBlock {
render() { return (<div></div>); }
};
class BlockTypeB extends GenericBlock {
render() { return (<div></div>); }
};
class BlockTypeC extends GenericBlock {
render() { return (<div></div>); }
};
export { BlockTypeA, BlockTypeB, BlockTypeC };
The above code snippet yields the error: Objects are not valid as a React child, but I think it captures the spirit of what I am talking about. Is there a way to make the above scheme work?
I am sure that this question has already been asked and answered, but I can't seem to get the Google Search correct.
EDIT:
Added Sandbox link: https://codesandbox.io/s/wizardly-wilson-vb7nn
Now I am encountering a new error: Type 'Element' is missing the following properties from type 'GenericBlock': render, context, setState, forceUpdate, and 2 more.
EDIT 2:
Typing the blocks array as JSX.Element removes the error and makes everything work, but that doesn't seem like very good practice as JSX.Element can be any element, whereas the point of typing it as GenericBlock is to ensure that all the elements are descendants of a specific component.
in react code, it works perfectly: https://codesandbox.io/s/optimistic-ptolemy-g25ge
no errors, no warnings
import * as React from "react";
import { BlockTypeA, BlockTypeB, BlockTypeC } from "./MyBlocks";
export default class BlockHolder extends React.Component {
blocks = [
<BlockTypeA key={1} />,
<BlockTypeB key={2} />,
<BlockTypeC key={3} />
];
render() {
return <div id="workspace">{this.blocks}</div>;
}
}
import * as React from "react";
export default class GenericBlock extends React.Component {
render() {
return <div />;
}
}
import * as React from 'react';
import GenericBlock from './GenericBlock';
class BlockTypeA extends GenericBlock {
render() { return (<div>A</div>); }
};
class BlockTypeB extends GenericBlock {
render() { return (<div>B</div>); }
};
class BlockTypeC extends GenericBlock {
render() { return (<div>C</div>); }
};
export { BlockTypeA, BlockTypeB, BlockTypeC };
"dependencies": {
"react": "16.12.0",
"react-dom": "16.12.0",
"react-scripts": "3.0.1"
},
"devDependencies": {
"typescript": "3.8.3"
}

Forwarding Refs: forwardedRef is always null

I have an ant design Table component that I want ref to be attached to.
I want to be able to use the tableRef in HOC withCustomPagination's lifecycle componentDidUpdate method.
Following the React Docs Forwarding Refs, that I could not clearly comprehend. I could cook up the following code:
App.js
import WrappedTable from '/path/to/file';
class App extends React.Component {
render() {
const tableRef = React.createRef();
return (
<WrappedTable ref={tableRef} />
)
}
}
Table.js
import withCustomPagination from '/path/to/file';
class Table extends React.Component {
constructor(props) {
super(props);
}
render() {
<TableContainer ref={this.props.forwardedRef} />
}
}
const WrappedTable = withCustomPagination(Table);
export default WrappedTable;
withCustomPagination.js
import CustomPagination from 'path/to/file';
const withCustomPagination = tableRef => Component => {
class WithCustomPagination extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: 1,
dataLength: props.dataLength,
}
}
componentDidUpdate() {
tableRef.current.state ..... //logic using ref, Error for this line
this.state.rows ..... //some logic
}
render() {
const { forwardedRef } = this.props;
return (
<Component {...this.state} ref={forwardedRef} />
<CustomPagination />
)
}
}
return React.forwardRef((props, ref) => {
return <WithCustomPagination {...props} forwardedRef={ref} />;
});
}
export default withCustomPagination;
After debugging, I find that forwardedRef is always null.
Your issue is happening in your HOC:
here
const withCustomPagination = tableRef => Component => {
You need to remove that parameter. The way to access to the ref prop is simply in your componentDidUpdate method like forwardedRef prop e.g:
import CustomPagination from 'path/to/file';
const withCustomPagination = Component => {
class WithCustomPagination extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: 1,
dataLength: props.dataLength,
}
}
componentDidUpdate() {
//You got the ref here
console.log(forwardedRef.current)
}
render() {
const { forwardedRef } = this.props;
return (
<Component {...this.state} ref={forwardedRef} />
<CustomPagination />
)
}
}
return React.forwardRef((props, ref) => {
return <WithCustomPagination {...props} forwardedRef={ref} />;
});
}
export default withCustomPagination;
Also somethings to have in account are:
You should not create the ref in the render method because this method is raised every time you set a state. I recommend you to do it in the constructor:
import WrappedTable from '/path/to/file';
class App extends React.Component {
constructor(){
super();
this.reference = React.createRef();
}
render() {
return (
<WrappedTable ref={this.reference} />
)
}
}
Also in you HOC render only one child or use React.Fragment. Besides do not forget the send the rest properties:
const withCustomPagination = Component => {
class WithCustomPagination extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: 1,
dataLength: props.dataLength,
}
}
componentDidUpdate() {
//You got the ref here
console.log(forwardedRef.current)
}
render() {
// Do not forget to send the rest of properties here like:
const { forwardedRef, ...rest } = this.props;
return (
<React.Fragment>
<Component {...this.state} ref={forwardedRef} {...rest} />
<CustomPagination />
</React.Fragment>
)
}
}
return React.forwardRef((props, ref) => {
return <WithCustomPagination {...props} forwardedRef={ref} />;
});
}
export default withCustomPagination;
EDIT:
Add the reference of the ref prop
import withCustomPagination from '/path/to/file';
class Table extends React.Component {
constructor(props) {
super(props);
this.reference = React.createRef();
}
render() {
<TableContainer ref={this.reference} />
}
}
const WrappedTable = withCustomPagination(Table);
export default WrappedTable;
To access the tableRef in HOC withCustomPagination, I removed const tableRef = React.createRef() from App.js and the corresponding ref = {tableRef} lines.
I pass tableRef to HOC, curried, withCustomPagination(tableRef)(NewGiftCardTable). I also removed all the Forwarding Refs logic in HOC, this I did because I needed access to tableRef only in HOC and not in App.js.
Added above removed lines to Table.js:
import withCustomPagination from '/path/to/file';
const tableRef = React.createRef();
class Table extends React.Component {
constructor(props) {
super(props);
}
render() {
<TableContainer ref={tableRef} />
}
const WrappedTable = withCustomPagination(tableRef)(Table);
export default WrappedTable;

Cannot read property 'state' of undefined in react-native context api?

I'm trying to context api in my ract-native app. But i'm getting this error.
TypeError: TypeError: Cannot read property 'number' of undefined. What is wrong my code?
appContext.js
import React from 'react';
export const AppContext = React.createContext();
class AppProvider extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 10,
};
}
render() {
return
(
<AppContext.Provider value={this.state}>
{this.props.children}
</AppContext.Provider>
)
}
}
export default AppProvider;
homeScreen.js
import { AppContext } from './appContext';
<AppContext.Consumer>
{(context) => context.number}
</AppContext.Consumer>
This is because you are not using your AppProvider component anywhere in your App. Try like this:
const AppContext = React.createContext();
class AppProvider extends React.Component {
constructor( props ) {
super( props );
this.state = {
number: 10,
};
}
render() {
return (
<AppContext.Provider value={this.state}>
{this.props.children}
</AppContext.Provider>
);
}
}
class App extends React.Component {
render() {
return (
<AppProvider>
<AppContext.Consumer>
{context => context.number}
</AppContext.Consumer>
</AppProvider>
);
}
}
AppProvider is merely a standard component here. It renders the context's Provider and that one gets some children. So, you should use this AppProvider component somewhere in your app and pass a child with a Consumer.
If you want to keep your context in a separate file it would be like this:
Context and provider component
import React from "react";
export const AppContext = React.createContext();
class AppProvider extends React.Component {
constructor( props ) {
super( props );
this.state = {
number: 1745,
};
}
render() {
return (
<AppContext.Provider value={this.state}>
{this.props.children}
</AppContext.Provider>
);
}
}
export default AppProvider;
Main component
import React from "react";
import AppProvider, { AppContext } from "./AppProvider";
class App extends React.Component {
render() {
return (
<AppProvider>
<AppContext.Consumer>
{context => context.number}
</AppContext.Consumer>
</AppProvider>
);
}
}
export default App;

React - Pass function to component that has default props

I have a React component that uses default props:
class MyComponent extends Component {
constructor(props) {
console.log('props', props);
super(props);
// rest of code here
}
MyComponent .defaultProps = {
__TYPE: 'MyDateRange',
};
When I use the component, without passing any props, the console log of props shows the default props, like it should.
Now, when I want to pass an additional prop (a function in this case), like this:
<MyComponent onEnterKey={() => console.log('snuh')}/>
The console log of props only shows the onEnterKey function.
What do I have to do to allow MyComponent to use the default props and accept a function? I've tried adding another argument to the constructor of MyComponent, but that doesn't work.
I tried and this is working :
import React from "react";
import { render } from "react-dom";
class MyComponent extends React.Component {
constructor(props) {
console.log("props", props);
super(props);
}
render() {
return null;
}
}
MyComponent.defaultProps = {
__TYPE: "MyDateRange"
};
render(
<MyComponent onEnterKey={() => console.log("snuh")} />,
document.getElementById("app")
);
You can see it here : https://codesandbox.io/s/wkw0k0j5o8
You can put the defaultProp on the class outside of the constructor like this:
class MyComponent extends Component {
constructor(props) {
console.log("props", props);
super(props);
}
render() {
return <div> test </div>;
}
}
MyComponent.defaultProps = {
__TYPE: "MyDateRange"
};
Alternatively, you can have defaultProps be a static property on the class:
class MyComponent extends Component {
static defaultProps = {
__TYPE: "MyDateRange"
};
constructor(props) {
console.log("props", props);
super(props);
}
render() {
return <div> test </div>;
}
}

Categories