Creating a non-rendered wrapper component in react.js - reactjs

I'm wanting to create a React component that does a security check and if that passes it'll render out the children of it, if it fails then it won't render anything.
I've scaffolded out a component like so:
var RolesRequired = React.createClass({
permitted: roles => ...,
render: function () {
if (!this.permitted(this.props.roles)) {
return null;
}
return this.props.children;
}
});
The usage I was planning would be like this:
<RolesRequired roles={['admin']}>
<h1>Welcome to the admin</h1>
<div>
Admin stuff here
</div>
</RolesRequired>
How would you return all the children from the RolesRequired component?

I came up with this solution:
var RolesRequired = React.createClass({
permitted: roles => ...,
render: function () {
if (!this.permitted(this.props.roles)) {
return null;
}
return <div>{this.props.children}</div>;
}
});
What I'm doing is wrapping the children being returned in a <div> but I'm having to add an unwanted/unneeded DOM element to achieve it.

I think higher order components (HOC) are also a good candidate for this. You can basically wrap any component in HOC that defines some behaviour and decides if it should render a wrappe.
Nicest way to do this would be if you're using a ES2015 transpiler with some ES2016 features enabled (namely decorators):
function withRoles(roles) {
return function(Component) {
return class ComponentWithRoles extends React.Component {
constructor(props) {
super(props)
// Not sure where the data to get your roles about current user?
// from but you could potentially to that here if I'm getting your point
// and also setup listeners
this.state = { currentUser: 'admin' }
}
validateRoles() {
// you have access to the ``roles`` variable in this scope
// you can use it to validate them.
return true;
}
render() {
if (this.validateRoles()) {
return <Component {...this.props} />;
)
} else {
return <div>Nope...</div>;
}
}
}
}
}
// You can then use this on any component as a decorator
#withRoles({ showOnlyFor: [ 'admin' ] })
class AdminOnlyComponent extends React.Component {
render() {
return <div> This is secert stuff </div>
}
}
I've used ES2016 features because I think it's nicer to get the point across but you can implement that with just a simple function wrapping, here's a gist by one of the React core members on the topic of HOC:
https://gist.github.com/sebmarkbage/ef0bf1f338a7182b6775

Related

Relay local state management. How to add and use flag on client side?

The problem:
I want to have simple boolean flag that will be true when modal is opened and false when it is closed. And I want to update other components reactively depends on that flag
I hope there is a way to do it with relay only (Apollo has a solution for that). I don't want to connect redux of mobx or something like that (It is just simple boolean flag!).
What I already have:
It is possible to use commitLocalUpdate in order to modify your state.
Indeed I was able to create and modify my new flag like that:
class ModalComponent extends PureComponent {
componentDidMount() {
// Here I either create or update value if it exists
commitLocalUpdate(environment, (store) => {
if (!store.get('isModalOpened')) {
store.create('isModalOpened', 'Boolean').setValue(true);
} else {
store.get('isModalOpened').setValue(true);
}
});
}
componentWillUnmount() {
// Here I mark flag as false
commitLocalUpdate(environment, (store) => {
store.get('isModalOpened').setValue(false);
});
}
render() {
// This is just react component so you have full picture
return ReactDOM.createPortal(
<div
className={ styles.modalContainer }
>
dummy modal
</div>,
document.getElementById('modal'),
);
}
}
The challenge:
How to update other components reactively depends on that flag?
I can't fetch my flag like this:
const MyComponent = (props) => {
return (
<QueryRenderer
environment={ environment }
query={ graphql`
query MyComponentQuery {
isModalOpened
}`
} //PROBLEM IS HERE GraphQLParser: Unknown field `isModalOpened` on type `Query`
render={ ({ error, props: data, retry }) => {
return (
<div>
{data.isModalOpened}
<div/>
);
} }
/>);
};
Because Relay compiler throws me an error: GraphQLParser: Unknown field 'isModalOpened' on type 'Query'.
And the last problem:
How to avoid server request?
That information is stored on client side so there is no need for request.
I know there a few maybe similar questions like that and that. But they doesn't ask most difficult part of reactive update and answers are outdated.
If you need to store just one flag as you said, I recommend you to use React Context instead of Relay. You could do next:
Add Context to App component:
const ModalContext = React.createContext('modal');
export class App extends React.Component {
constructor() {
super();
this.state = {
isModalOpened: false
}
}
toggleModal = (value) => {
this.setState({
isModalOpened: value
})
};
getModalContextValue() {
return {
isModalOpened: this.state.isModalOpened,
toggleModal: this.toggleModal
}
}
render() {
return (
<ModalContext.Provider value={this.getModalContextValue()}>
//your child components
</ModalContext.Provider>
)
}
}
Get value from context everywhere you want:
const MyComponent = (props) => {
const { isModalOpened } = useContext(ModalContext);
return (
<div>
{isModalOpened}
</div>
);
};
If you will use this solution you will get rid of using additional libraries such as Relay and server requests.

Is this considered mutation from a Higher Order Component?

I was reading the section on Don’t Mutate the Original Component. Use Composition from this link.
https://reactjs.org/docs/higher-order-components.html
I then reviewed a project I'm trying to build. At a high level, this is what my code looks like:
class Wrapper extends Component {
constructor(props) {
this.wrappedComponent = props.wrappedComponent;
}
async componentWillAppear(cb) {
await this.wrappedComponent.prototype.fetchAllData();
/* use Greensock library to do some really fancy animation on the wrapper <Animated.div> */
this.wrappedComponent.prototype.animateContent();
cb();
}
render() {
<Animated.div>
<this.wrappedComponent {...this.props} />
</Animated.div>
}
}
class Home extends Component {
async fetchAllData(){
const [r1,r2] = await Promise.All([
fetch('http://project-api.com/endpoint1'),
fetch('http://project-api.com/endpoint2')
]);
this.setState({r1,r2});
}
animateContent(){
/* Use the GreenSock library to do fancy animation in the contents of <div id="result"> */
}
render() {
if(!this.state)
return <div>Loading...</div>;
return (
<div id="result">
{this.state.r1.contentHTML}
</div>
);
}
}
export default class App extends Component {
render() {
return <Wrapper wrappedComponent={Home} />;
}
}
My questions are:
In my Wrapper.componentWillAppear(), I fire the object methods like this.wrappedComponent.prototype.<methodname>. These object methods can set it's own state or animate the contents of the html in the render function. Is this considered mutating the original component?
If the answer to question 1 is yes, then perhaps I need a better design pattern/approach to do what I'm trying to describe in my code. Which is basically a majority of my components need to fetch their own data (Home.fetchAllData(){then set the state()}), update the view (Home.render()), run some generic animation functions (Wrapper.componentWillAppear(){this.animateFunctionOfSomeKind()}), then run animations specific to itself (Home.animateContent()). So maybe inheritance with abstract methods is better for what I want to do?
I would probably actually write an actual Higher Order Component. Rather than just a component which takes a prop which is a component (which is what you have done in your example). Predominately because I think the way you have implemented it is a bit of a code smell / antipattern.
Something like this, perhaps.
class MyComponent extends React.Component {
constructor() {
super();
this.animateContent = this.animateContent.bind(this);
}
componentWillReceiveProps(nextProps) {
if (this.props.r1 !== nextProps.r1) {
this.animateContent();
}
}
componentDidMount() {
// do your fetching and state setting here
}
animateContent() {
// do something
}
render() {
if(!this.props.r1) {
return <div>Loading...</div>;
}
return (
<div id="result">
{this.props.r1.title}
</div>
);
}
}
const myHOC = asyncFn => WrappedComponent => {
return class EnhancedComponent extends React.Component {
async componentDidMount(){
const [r1, r2] = await asyncFn();
this.setState({ r1, r2 })
this.animateContent();
}
animateContent = () => {
// do some animating for the wrapper.
}
render() {
return (<WrappedComponent {...this.props} {...this.state} />)
}
}
}
const anAsyncExample = async () => {
const result = await fetch("https://jsonplaceholder.typicode.com/posts");
return await result.json();
}
const MyEnhancedComponent = myHOC(anAsyncExample)(MyComponent);
Here's a working JSFiddle so you can see it in use:
https://jsfiddle.net/patrickgordon/69z2wepo/96520/
Essentially what I've done here is created a HOC (just a function) which takes an async function and returns another function which takes and a component to wrap. It will call the function and assign the first and second result to state and then pass that as props to the wrapped component. It follows principles from this article: https://medium.com/#franleplant/react-higher-order-components-in-depth-cf9032ee6c3e

Passing function from props to outside of the component (High Order Component)

I'm trying to pass the function from high order component outside the class because I need to call it but it is also needed to be pass back. Hard to explain, here's the code:
Wrapped Component:
class foo extends React.Component {
....
}
foo.list = [
{
name: "Home",
action: this.props.funcFromHoc //this.props.from foo class, how can i access this because it is outside the component?
}
]
export default bar(foo);
High Order Component:
export default function bar(WrappedComponent) {
funcFromHoc() {
alert("Hello from HOC function!");
}
render() {
return (
<WrappedComponent
{ ...this.props }
funcFromHoc={ this.funcFromHoc }
);
}
}
What I'm actually doing:
I have a base screen (HOC) with a 2 drawers, that has some functions that controls their behavior. I need this 2 drawers on many screens that I'll make, I don't want to put the configuration of the drawer for every screens, that's why I create a HOC for this. My problem is, the list on the drawer on HOC is dynamic on each screens, and they have specific function that I set on each screens, how can I pass a function from the screen component to HOC?
Am I missing something or this? Am I doing it wrong? Did I missed some of the proper usage of High Order Components? Or what method should I use for this? Any hint or help will be very much appreciated. Thank you.
I've found a solution, it solves my problem using Inheritance Inversion.
class foo extends React.Component {
list() {
return [
{
name: "Home",
action: this.funcFromHoc //successfully accessed it here!
}
];
}
}
export default bar(foo);
(High Order Component):
export default function bar(WrappedComponent) {
return class Bar extends WrappedComponent {
funcFromHoc() {
alert("Hello from HOC function!");
}
render() {
return (
//call super.list() here to populate drawer list
{super.render()}
);
}
}
}
It sounds like you want to be able to pass additional parameters to the HOC. You could pass a mapping function when you call the HOC, like the color variable below. Note the HOC is defined slightly differently as well in order to take additional arguments.
edit: nevermind, you won't have access to funcFromFoo unless it is a prop of foo (i.e. defined in mapDispatchToProps). heh- i tried. may need to rethink the design so this isn't a requirement.
function doSomethingWithList(props) {
const { actionName, funcFromHOC, funcFromFooProps } = props;
if (actionName === 'Home') {
funcFromHOC();
} else {
funcFromFooProps();
}
};
const makeToggleable = (WrappedComponent, color) => {
return class ToggleableComponent extends React.Component {
constructor(props) {
super(props);
this.state = { toggled: false };
this.toggleColor = this.toggleColor.bind(this);
}
toggleColor() {
this.setState({ toggled: !this.state.toggled });
}
render() {
const fontColor = this.state.toggled? color: 'black';
return (
<WrappedComponent { ...this.props }
style={{color: fontColor}}
onClick={this.toggleColor} />
);
}
}
}
export default makeToggleable(BaseComponent, 'red');
const listItems = [ 'Home', 'Other' ];
export default bar(foo, listItems, doSomethingWithList);
pass listItems and doSomethingWithList for OP's question, modify HOC to call doSomethingWithList with props
example code from https://spin.atomicobject.com/2017/03/02/higher-order-components-in-react/

Access parent context when using this.props.children in React

Given the following, is it possible to access the parent context rather than the containers from a child (non-react component) element?
The example logs container, ideally it would log parent. I would like for Parent to be self contained, not to have it's state managed by its container.
var Container = React.createClass({
getInitialState: function () {
return {
context: 'container'
}
},
render: function () {
return (
<Parent>
<a href="#" onClick={function () {console.log(this.state.context);}.bind(this)}>click me</a>
</Parent>
);
}
});
var Parent= React.createClass({
getInitialState: function () {
return {
context: 'parent'
}
},
render: function () {
return (
<div>
{this.props.children}
</div>
);
}
});
If there is another pattern for handling this, please share as well.
Note: To be clear, I understand how the this keyword works and why the above example works as it does. The example is simply meant to illustrate the problem.
You can import some React helpers for that:
var React = require('react')
...
var children = React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
context: this.state.context
})
})
render() {
return <div>{ children }</div>
}
...
Then your child component will have this.props.context which will be the string 'parent', but this must be a React component, as this needs to refer to the component using the parent prop
var YourComponent = React.createClass({
render() {
return (
<a href="#" onClick={() => console.log(this.props.context)}>
click me
</a>
)
}
})
------
var Parent = require('./Parent')
var YourComponent = require('./YourComponent')
...
render() {
return <Parent><YourComponent /></Parent>
}
I do not know about the first part of your question, but since you commented about dynamically creating components, here's how I do it:
You can set a state variable in the constructor of the class and its parent:
if (typeof this.state == 'undefined') {
this.state = {
componentsToRender: <div></div>
};
}
Then in the parent component, in the componentDidMount() function:
var componentsToRender = [];
if ([conditional]) {
// some logic so you know which component to render
componentsToRender.push(<customChildComponentToRender key={} />);
}
else {
componentsToRender.push(<otherComponentToRender key={} />);
}
this.setState({
componentsToRender: <div>{componentsToRender}</div>
});
Make sure to put a key (lines 4 and 7 of the second code block) or React will scream at you.
In response to your initial question, I would watch this video from the ReactJS Conference 2015 to get more of the heart behind a container. After hearing what the guys at Facebook say (who have radical views on containers!), you might want to rethink the design to make your container more of a data layer.
I would check out THIS article from the react website. I think it might give you some intuition on solving your problem.
As a general rule of thumb, I try and only use this.state to handle internal UI state of a specific component. Everything else is passed via props. If you're needing the full context of a component, I would either pass it as a prop or checkout something like flux or redux which will help you manage state between components.

How can I use dynamic react class element in render?

I understand the question is not clear, so the description will help.
So, I have some react components like,
var LandingPage = React.createClass({
render: function() {
return <div>
This is the landing page.
</div>
}
})
and another component like
var FirstContent = React.createClass({
render: function() {
return <div>
This is first content page
</div>
}
})
Now I have a controller where I decide which one I need to render by passing a value in props, something like this -
var Contoller = React.createClass({
render: function() {
var inside = "";
if (this.props.pageName == "LandingPage") {
inside = <LandingPage />;
} else if (this.props.pageName == "FirstContent") {
inside = <FirstContent />;
}
return <div>
{inside}
</div>;
}
})
Now instead, I want to do something like, use the this.props.pageName inside the tag directly, so that I don't have to write if else for every time ad some new alternate content. Should be something like this -
var Contoller = React.createClass({
render: function() {
return <div>
<"this.props.pageName" /> //comment - <LandingPage /> if this.props.pageName = "LandingPage"
</div>;
}
})
The map of pageName to actual Component has to exist somewhere, because other than the default HTML elements (like div) React needs the class object reference to render a component. A string wont do.
How you manage this map is up to you, but I've used an object below.
Further complicating this is the JSX compilation step, which doesn't work with dynamic content. You will have to use the actual JS calls in your Controller to get this working.
Here is a codepen demonstrating this.
class LandingPage extends React.Component {
render() {
return <div> This is the landing page. </div>;
}
}
class FirstContent extends React.Component {
render() {
return <div> This is the first content page. </div>;
}
}
const map = {
LandingPage: LandingPage,
FirstContent: FirstContent
};
class Controller extends React.Component {
render() {
return React.createElement(map[this.props.pageName]);
}
}
React.render(<Controller pageName={'LandingPage'} />, document.body);
All that being said, I think you are building a router. You can use react-router in memory mode to perform routing without using the URL. Rolling your own setup here may be more work than it's worth.
The map does exist in the example by Tyrsius: you can use window[this.props.pageName]. Though it's better not to expose your components to the window object. And it may not work at all if you're using CommonJS for your React components.
If you don't need to build a name of several parts, why don't you just pass the component itself instead of a string? Either as a property or, better, as a child:
class Controller extends React.Component {
render() {
return <div>{this.props.children}</div>;
}
}
React.render(<Controller><FirstContent/></Controller>, document.body);

Resources