How to get the data from React Context Consumer outside the render - reactjs

I am using the new React Context API and I need to get the Consumer data from the Context.Consumer variable and not using it inside the render method. Is there anyway that I can achieve this?
For examplify what I want:
console.log(Context.Consumer.value);
What I tested so far: the above example, tested Context.Consumer currentValue and other variables that Context Consumer has, tried to execute Context.Consumer() as a function and none worked.
Any ideas?

Update
As of React v16.6.0, you can use the context API like:
class App extends React.Component {
componentDidMount() {
console.log(this.context);
}
render() {
// render part here
// use context with this.context
}
}
App.contextType = CustomContext
However, the component can only access a single context. In order to use multiple context values, use the render prop pattern. More about Class.contextType.
If you are using the experimental public class fields syntax, you can use a static class field to initialize your contextType:
class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
/* render something based on the value */
}
}
Render Prop Pattern
When what I understand from the question, to use context inside your component but outside of the render, create a HOC to wrap the component:
const WithContext = (Component) => {
return (props) => (
<CustomContext.Consumer>
{value => <Component {...props} value={value} />}
</CustomContext.Consumer>
)
}
and then use it:
class App extends React.Component {
componentDidMount() {
console.log(this.props.value);
}
render() {
// render part here
}
}
export default WithContext(App);

You can achieve this in functional components by with useContext Hook.
You just need to import the Context from the file you initialised it in. In this case, DBContext.
const contextValue = useContext(DBContext);

You can via an unsupported getter:
YourContext._currentValue
Note that it only works during render, not in an async function or other lifecycle events.

This is how it can be achieved.
class BasElement extends React.Component {
componentDidMount() {
console.log(this.props.context);
}
render() {
return null;
}
}
const Element = () => (
<Context.Consumer>
{context =>
<BaseMapElement context={context} />
}
</Context.Consumer>
)

For the #wertzguy solution to work, you need to be sure that your store is defined like this:
// store.js
import React from 'react';
let user = {};
const UserContext = React.createContext({
user,
setUser: () => null
});
export { UserContext };
Then you can do
import { UserContext } from 'store';
console.log(UserContext._currentValue.user);

Related

How to prevent React component mounting if props are empty?

I have a component which works with third party library and I need to add listener after component mounts. For some reason I can't add listeners without data which asynchronously fetched from server and passed through 'connect' function (from react-redux).
How to prevent React component mounting if props are empty?
After some research I haven't found a solution, so I wrote mine:
// No Props No Mount
import React from 'react';
import _ from 'lodash';
function NPNM(WrappedComponent) {
return class extends React.Component {
render() {
const { children } = this.props;
const data = _.omit(this.props, children);
let hasProps = true;
_.forEach(data, elm => {
if (_.isEmpty(elm) && !_.isFunction(elm)) hasProps = false;
});
return hasProps ? <WrappedComponent {...this.props} /> : <></>;
}
};
}
export default NPNM;
It should be used like
connect(mapStateToProps, mapDispatchToProps)(NPNM(YourComponent));

Best way to sharing function in react

I'm new in react. I wonder what is the best option for sharing functions (except inheritance). In inheritance way I can do something like this:
class BaseForm {
updateForm() => {};
}
class MyFormOne extends BaseForm {
// this.updateForm();
}
And I can code few Form components which extends BaseForm and I can use updateForm method in every. What is the best way in react to do this?
There are multiple ways for the question :
1) If the shared fnc is the static function (independent with the component). You should create a common file utils.js and then export, import function wherever you want
// utils.js
export function updateForm() { }
// MyformOne.js
import {updateForm} from './utils';
// Use fnc updateForm() in MyFormOne component
2) If the shared fnc is dependent with class component, you should use the design pattern in React : HOC (Higher Order Component) or render Props for sharing code (states, functions) between React components (render your code more abstract => a good practice for reusable)
class BaseForm extends React.Component {
constructor(props) {
super(props);
this.state = {
a1 : "",
a2 : "",
}
}
// example : share the func updateForm of Baseform
updateForm = () => {}
render() {
return (
<div>
{/*
use the `render` prop to dynamically determine what to render.
*/}
{this.props.render(this.state, this.updateForm)}
</div>
);
}
}
class WithBaseForm extends React.Component {
render() {
return (
<div>
<BaseForm render={(stateBaseForm, updateForm) => (
<MyFormOne stateBaseForm={stateBaseForm} updateForm={updateForm}/> // so MyFormOne could use the fnc updateForm of BaseForm
)}/>
</div>
);
}
}
You should view the link for more details :
https://en.reactjs.org/docs/render-props.html
https://en.reactjs.org/docs/higher-order-components.html
You can create a utils.js file and place your common JS functions there. Something like this:
export const updateFormCommon = (inputValue) => {
// Your function logic here
return something;
};
export const showMessage = () => {
alert('hello world');
};
Now you can import utils.js wherever you need and call your common\shared functions like this:
import {updateFormCommon, showMessage} from './utils'
let result = updateFormCommon(1000)}
showMessage();

React - What is meant by 'Do not use HOC’s in the render method of a component. Access the HOC outside the component definition.'?

I am learning HOCs and keep reading the above quote, but I do not understand what it means. If my HOC adds a method to my consuming component, can I use that method in the render method like so? If not how would I do what I am trying to do here:
import React, { Component } from 'react';
import { withMyHOC } from '../with_my_component'
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default withMyHOC(MyComponent );
When you say, do not use HOC within the render method, it means that you shouldn't create an instance of the component wrapped by HOC within the render method of another component. For example, if you have a App Component which uses MyComponent, it shouldn't be like below
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default MyComponent;
import { withMyHOC } from '../with_my_component'
export default class App extends React.Component {
render() {
const Wrap = withMyHOC(MyComponent);
return (
<div>
{/* Other Code */}
<Wrap />
</div>
)
}
}
Why you shouldn't use it like above is because everytime render method is called a new instance of the MyComponent is created wrapped by HOC called Wrap and hence everytime it be be mounted again instead of going by the natural lifecycle or React.
However if your HOC passes a function as props, you can use it within the render as long as it doens't cause a re-render again otherwise it will lead to a infinite loop.
Also its better to memoize functions which are called in render directly to avoid computation again and again
CodeSandbox Demo
A High Order Component is a function which returns a Component, not jsx. When wrapping a component with an hoc, you're not changing the returned value of your component, you're changing the signature itself. Consider the following hoc
const withFoo = Component => props =>{
return <Component {...props} foo='foo' />
}
withFoo is a function which takes a Component (not jsx) as argument and returns a component. You don't need to call an hoc from render because the values it injects are already inside props of the wrapped component.
An hoc tells how a wrapped component will look like, changes it's definition so the only place to use it is in the component definition itself. Calling an hoc inside render creates a new instance of that component on each render. It's the equivalent of
const Component = () =>{
const ChildComponent = () =>{
return <span> Child </span>
}
return <ChildComponent /> //You're declaring again on each render
}
Use your high order components like this
const Component = ({ foo }) => <div>{ foo }</div>
export default withFoo(Component)
Or
const Component = withFoo(({ foo }) => <div>{ foo }</div>)

Pass dynamic value to HOC in react

I write some HOC and I need to pass to this HOC a dynamic object that I create on some life cycle level and I did not get him as a prop.
If I try to pass some static value ( for example initialize myObj from start) it works as expected and I get the correct value.
Let's say this is my component class :
let myObj = {};
class Test extends React.Component
{
constructor(props) {
super(props);
.....
}
render() {
myObj = {test:'test'};
return ( ... )
}
}
export default withHOC(Test, myObj);
And this is my HOC:
const withHOC = (Component, test) => {
class Hoc extends React.Component
{
constructor(props)
{
super(props);
const s = test; // ---->test is empty object always !!
...
}
}
return Hoc;
}
My 'Dynamic' object that I create on my 'test' class is always empty on my HOC class.
It's happend also when I try to pass some value from my props directly, in this case the page is stuck(without errors in console).
Does someone have any idea how to resolve that? Thanks!
When you compose a component that way, composition only happens at compile time (static composition). This means that withHOC runs only once and is receiving an empty myObj argument, as it is using the one defined on declaration.
export default withHOC(Test, myObj); //myObj = {}
If you want that value to be dynamic, the withHOC composition should be runned when that value changes.
You can't send data up from the WrappedComponent (Test) to the HOC (withHOC), so even if you change myObj value in Test.render, the HOC would never know.
What you could do, if you really need it, is do the composition on the Test.render
render(){
const Hoc = withHOC(this.state.myObj, WrappedComponent);//WrappedComponent can be anything
return(
<Hoc/>
)
}
This way, every time the component renders, Hoc is composed using as myObj a value from the component state, wich is not the preferable way to do it, because this.state.myObj might have the same value as it did at the previous render, and you would be re-composing with the same data as before.
A better way to do it is checking for changes in myObj at Test.componentDidUpdate, and if it did change, then compose Hoc again.
You are passing an empty object to the withHOC function
let myObj = {}; // <- your myObj is empty
class Test extends React.Component
{
constructor(props) {
super(props);
.....
}
render() {
myObj = {test:'test'}; // <- You're doing this in the render method of your Test component, so until the component is rendered myObj is empty
return ( ... )
}
}
export default withHOC(Test, myObj);
Some explanation about what's happening here, by order:
import Comp from '.../Test.js'
the withHOC function is triggered, with the params of Test (which is defined above the call) and myObj (which is defined above the call but is empty)
Test component is returned, and nobody used the logic of myObj = {test:'test'}
Suggested solution:
Make the HOC get the logic from the props with another hoc:
const withProps = newProps => BaseComponent => props => {
const propsToAdd = typeof newProps === 'function' ? newProps(props) : newProps
return <BaseComponent {...props} {...propsToAdd} />
}
Usage:
class Test extends React.Component
{
constructor(props) {
super(props);
.....
}
render() {
return ( ... )
}
}
export default withProps({test:'test'})(withHOC(Test));
// or: export default withProps(props => {test:'test'})(withHOC(Test));
const withHOC = (Component) => {
class Hoc extends React.Component
{
constructor(props)
{
super(props);
const s = this.props.test;
...
}
}
return Hoc;
}
you can use recompose, a library which has many hocs and utils, and for better readability:
import { compose, withProps } from "recompose"
class Test extends React.Component {...}
const enhance = compose(
withProps({test:'test'}),
withHOC
)
export default enhance(Test);
I can't say with confidence this is optimal but I solved a similar problem by having a function within the HOC that updates state that you can then invoke with any data in the wrapped component.
HOC:
func = (a, b) => {
this.setState({
stateA: a,
stateB: b
)}
}
return ({ <WrappedComponent func={this.func} /> })
Wrapped Component:
this.props.func(anythingA, anythingB);
You can then access the data through state in the HOC.
To elaborate:
const withHOC = (WrappedComponent) => {
class withHOC extends React.Component {
constructor(props) {
super(props)
this.state = {
stateA: 1,
stateB: 2
}
*use state however you want in this HOC, including pass it through to another component*
*the following is just a function*
*when it's invoked in the wrapped component state will update here in the
HOC*
changeState = (a, b) => {
this.setState({
stateA: a,
stateB: b
)}
}
render() {
return (
<div>
<p>this.state.stateA</p>
<p>this.state.stateB</p>
<WrappedComponent changeState={this.changeState} />
</div>
)
}
}
}
}
In wrappedComponent, after importing:
class aComponent extends Component {
constructor(props) {
super(props)
this.state = {
}
*you can now invoke the function from the HOC in this wrapped component*
}
}
You can use react-redux and store your object in redux state. Change the object wherever you need (in your case it's in Test) and access it in component inside your HOC from redux state, it'll be always up to date.

Meteor loading data with React Komposer

I'm trying to load data using React Komposer and I'm not sure what I'm doing wrong, pretty sure this is the way it should be unless I miss something. But I'm not getting any data in the UI. Could use the help
container.js
import { composeWithTracker } from 'react-komposer';
import RightNavBar from './right-nav-bar.jsx';
function composer(props, onData) {
const subscription = Meteor.subscribe('currentUser');
const currentUser = 'bbbb';
onData(null, currentUser);
}
export default composeWithTracker(composer)(RightNavBar);
My component
export class RightNavBar extends React.Component {
render() {
return (
<div>
aaaa {currentUser}
</div>
);
}
}
Here is the "standard" example from react-komposer's repository (adapted to your specific case)
function composer(props, onData) {
const subscription = Meteor.subscribe('currentUser');
if (subscription.ready()) {
const currentUser = Meteor.user(); //or whatever
onData(null, {currentUser});
};
};
Here you subscribe and when the subscription is ready, your component is rendered. Otherwise, a loading component is rendered.
The 2nd parameter to onData should be an object. It is merged with other props passed to your component and is accessible from within your component via this.props.
From within your component,the props object is available via this.props, so you can either deconstruct it or access its properties directly.
class RightNavBar extends React.Component {
render() {
const {currentUser} = this.props;
return (
<div>
Hello, {currentUser.name}!
</div>
);
}
}
Your code sends a string rather than an object and React has no way of making sense of the token currentUser from within your component.

Resources