Enforcing RBAC to React components - javascript

If we have a React component lets say <App /> that is being rendered in another react component.
Can I have<App doSomething/> so that I can call the function doSomething before it is rendered?
Sorry, to add on the above question - I want to declare doSomething outside in a library so that I can import and call it before the React component <App doSomething/> is rendered.

Here you can read about lifecycle methods. https://reactjs.org/docs/react-component.html#the-component-lifecycle
Time before componentWillMount() was what you are looking for. Now it is deprecated.
According to the diagram that was linked in the react docs page, the place to make that calls can be the constructor
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
Also you can set an default boolean to false, and in componentDidMount do what you want and set the boolean to true, but in the render method check if that boolean is true and render your things

Yes
In this case doSomething is a prop that can be a function. Using React lifecycle methods you can call this function before the component renders.
From the react docs:
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update
(Since ComponentWillMount is deprecated).
EX:
class App extends React.Component {
static getDerivedStateFromProps(props) {
props.sayHello();
return null;
}
render() {
console.log("Rendering");
return <div>Hello {this.props.name}</div>;
}
}
class Hello extends React.Component {
sayHello() {
console.log("Hello!");
}
render() {
return <App sayHello={this.sayHello} />;
}
}
ReactDOM.render(
<Hello />,
document.getElementById('container')
);
<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>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
Edit:
It doesn't matter where the doSomething function comes from, you can pass it as a prop, or call it directly.

Related

Use react-i18next in class components with decorators and HOC

I'm trying to implement i18n in my React project which also uses Redux with the help of react-i18next.
In this project, we use class components with decorators.
Initially, I wanted to try react-i18next v10 but since it's relying on hooks and I cannot use those in class components, it's out of the question for me.
Falling back to the legacy v9, I followed the step by step guide (https://react.i18next.com/legacy-v9/step-by-step-guide) and performed the following steps :
Created the i18n.js config file with translations
Wrapped my root container with i18nextProvider
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<I18nextProvider i18n={i18n}>
<RootContainer />
</I18nextProvider>
</Router>
</Provider>,
$root,
);
Wrapped one simple component, which is a child of the RootContainer, with the withNamespaces() HOC, but with decorator syntax
#withNamespaces()
export default class SimpleComponent extends React.PureComponent {
// (... component class code ...)
}
Which is equivalent without decorators to the following :
class SimpleComponent extends React.PureComponent {
// (... component class code ...)
}
export default withNamespaces()(SimpleComponent);
I must be missing something since I get the following error during SSR :
react-i18next:: You will need pass in an i18next instance either by props, using I18nextProvider or by using i18nextReactModule. Learn more https://react.i18next.com/components/overview#getting-the-i-18-n-function-into-the-flow
0|ssr-dev | TypeError: Cannot read property 'wait' of null
0|ssr-dev | at NamespacesConsumerComponent.render (/client/node_modules/react-i18next/dist/commonjs/NamespacesConsumer.js:220:33)
Problem is, I'm no longer having this error if I remove the #withNamespaces() HOC on my component class, but then I do not have {t, i18n} in the component's props and therefore cannot translate anything.
Moreover, the given URL in the error unfortunately doesn't exist anymore.
As I understood from the documentation, the <I18nextProvider> is supposed to pass the {t, i18n} values down the component tree, which is not in my case.
I find myself stuck between not being able to use v10 (we have a lot of components and I can't just refactor them all to functional components at the moment) and not being able to make v9 work either.
I'm running out of options and could use some hindsight if you ever encountered similar issues.
Thanks in advance.
For anyone wondering, I got it working by wrapping the <RootContainer> child instead of wrapping <RootContainer> itself in the renderer method, which leaves me with something like the following :
Renderer
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<RootContainer />
</Router>
</Provider>,
$root,
);
RootContainer
import i18n from "../core/translations/i18n";
import {I18nextProvider} from "react-i18next";
#connectWithStore(mapStateToProps, mapActionsToProps)
export default class RootContainer extends React.PureComponent {
// (... class code ...)
render {
return (
<I18nextProvider i18n={i18n}>
<div>
<SimpleComponent/>
</div>
</I18nextProvider>
);
}
}
SimpleElement
import {withNamespaces} from "react-i18next";
#withNamespaces()
export default class SimpleComponent extends React.PureComponent {
// (... class code ...)
componentDidMount() {
// Successfully logging the values below
console.warn("Got i18n props?", {
t: this.props.t,
i18n: this.props.i18n,
});
}
}
The only difference being that my renderer is a function, not a component per se, whereas RootContainer is a fully-fledged React component (PureComponent, to be precise). Maybe it has to do with render chaining, not sure, but it still works as intended that way.
I managed to use the last version of react-i18next in class by doing like this :
import { withTranslation } from 'react-i18next';
class RootContainer extends React.Component {
constructor(props) {
super(props);
}
render() {
<span>{this.props.t('Home')}</span>
}
}

Pass React Props to a Separate JS File

I'm trying to pass props for width from a parent component to a child JS file, but cannot seem to get the props value in the child JS file. The parent is the following:
import React from 'react';
import Child from './Child';
export default class Home extends React.Component {
state = {
width: 1000
}
render(){
return(
<div>
<Child width={this.width} />
</div>
);
}
}
The separate child JS file is the following:
import React from 'react';
const svgWidth = 650, // Load prop here
svgHeight = 340;
What I've tried is the following but is not working for me:
import React from 'react';
const Child = (props) => {
console.log({props.width}); // Getting an error that ',' is expected
return {props.width};
}
Can someone please help me with passing the width value from ?
Change to the following, as you should access state through this.state, like so:
<Child width={this.state.width} />
Either use the prop drilling, hence pass the values from parent to child as a prop.
But exact answer to your question will be :
create a blank object in separate js file and export it and then in componentDidMount populate that object with the props which you want to save. Next time when ever you will use that object anywhere in normal js file you will get the props.
If you intend to pass props to a child component, then I would start with the following:
import React from 'react';
import Child from './Child';
export default class Home extends React.Component {
constructor(props) { // don't forget to add a constructor
super(props); // also required
this.state = {
width: 1000
}
}
render(){
// to pass state use {this.state}
return(
<div>
<Child width={this.state.width} />
</div>
);
}
}
However, if that is not the case and instead you want to export state to a separate js file (that may not even be a React component), then you may have to look at export syntax. I am struggling with a similar problem right now and I already tried what Vikash Kumar suggested without success. This is explained on this question but I was not successful with that approach either: export function inside react component or access state in same file outside of component

Stateful and Functional stateless components

How to convert Stateless functional components to stateful component in order to use lifecycle methods and passing props to it like Stateless Component.
( export default JobCard = (props) => {
............
}
)
I need to convert this to stateful component in order to use life-cycle method and pass props to the return function like how props is passed here. Thank you in advance.
You can do it like this:
export default class JobCard extends React.Component {
render() {
// here you can access props passing down from
// parent components like: this.props
return (
<div>
Hi, I'm a super smart component!
</div>
);
}
}

Self invoking function in react js ? (as in regular javascript)

I want to invoke the function good without calling it from a event. It should run as soon as page opened just like in the self invoking javascript function.
Here is an example
import React from 'react';
class App extends React.Component {
good(){
console.log('I was triggered during good')
}
render() {
console.log('I was triggered during render')
return(
<div>
good();
</div>
);
}
}
export default App;
Few Points:
1. You need to use this keyword to call any function from any other function.
2. To put js code inside JSX, we need to use {}.
Write it like this:
import React from 'react';
class App extends React.Component {
good(){
console.log('I was triggered during good')
return <div> Hello </div>
}
render() {
console.log('I was triggered during render')
return(
<div>
{this.good()}
</div>
);
}
}
Check React DOC: https://facebook.github.io/react/docs/introducing-jsx.html
Check these answers for more details:
How does the "this" keyword work?
What do curly braces mean in JSX (React)?
You can also use lifecycle methods as componentDidMount(){} or componentWillMount(){}.
componentWillMount will be triggered before mounting of component and componentDidMount() will be triggered after component has been mounted.

React.js How to render component inside component?

I am stuck. I have several seperate components on seperate files. If I render them in main.jsx like following:
ReactDOM.render(<LandingPageBox/>, document.getElementById("page-landing"));
ReactDOM.render(<TopPlayerBox topPlayersData={topPlayersData}/>, document.getElementById("wrapper profile-data-wrapper"));
ReactDOM.render(<RecentGamesBox recentGamesData={recentGamesData}/>, document.getElementById("history wrapper"));
Everything works fine, but I wonder if it is a good practice? Maybe it is possible to do something like there would be only one ReactDom.render like:
ReactDOM.render(<LandingPageBox recentGamesData={recentGamesData} topPlayersData={topPlayersData}/>, document.getElementById("page-landing"));
I tried different kinds of variatons of LandingPageBox to somehow include those other two components, but had no luck. They sometimes rendered outside the page and so on. I thought it should look something like this:
import React from 'react';
import RecentGames from '../RecentGames/RecentGames.jsx';
import TopPlayers from '../TopPlayers/TopPlayers.jsx';
import PageTop from './PageTop.jsx';
import PageBottom from './PageBottom.jsx';
class LandingPageBox extends React.Component {
render() {
return (
<body className="page-landing">
<PageTop>
<TopPlayers topPlayersData={this.props.topPlayersData} />
</PageTop>
<PageBottom>
<RecentGames recentGamesData= {this.props.recentGamesData}/>
</PageBottom>
</body>
);
}
}
export default LandingPageBox;
But this code only renders PageTop and PageBottom, without player or game components.
So my question would be, how to set up LandingPageBox file so that TopPlayers component would render inside PageTop component and RecentGames component would render inside PageBottom component? Thank you.
In your example
return (
<body className="page-landing">
<PageTop>
<TopPlayers topPlayersData={this.props.topPlayersData} />
</PageTop>
<PageBottom>
<RecentGames recentGamesData= {this.props.recentGamesData}/>
</PageBottom>
</body>
);
React will only render the top-level custom components PageTop and PageBottom, as you already found out. The other components (TopPlayers and RecentGames) are nested within those components. What does that mean? React does not just display those nested components because it would not know how to do this. Instead, all rendering must be done by the outer components PageTop and PageBottom. React just passes the nested components to them (PageTop gets TopPlayers, PageBottom gets RecentGames) in this.props.children. Now it is up to the outer components what to do with these nested components. In your example, you would modify the PageTop and PageBottom components to use {this.props.children} to display their nested components in a suitable way.
You are right. You can use as many nested components as you want. It's one of the main concepts in react.
You can access them in this.props.children.
Do it like this:
var Parent = React.createClass({
render: function() {
return <div>{this.props.children}</div>;
}
});
ReactDOM.render(
<Parent>
<Child/>
<Child/>
</Parent>,
node
);
Read more here - https://facebook.github.io/react/docs/multiple-components.html
And here - http://buildwithreact.com/article/component-children
Here Car component is inside the another component i.e Garage components.
When Garage component in rendering Car component is also renders.
Same concept as like one function inside another function.
class Car extends React.Component {
render() {
return <h2>I am a Car!</h2>;
}
}
class Garage extends React.Component {
render() {
return (
<div>
<h1>Who lives in my Garage?</h1>
<Car />
</div>
);
}
}
ReactDOM.render(<Garage />, document.getElementById('root'));

Categories