sharing a common state among duplicate react component in React JS - javascript

I have one doubt regarding state of component. I have one tile component. On click of tile component I am toggling the details of tile component.
/* ---- Tile component ---- */
import React, { Component } from 'react';
import TileDetail from './TileDetail';
class Tile extends Component {
constructor(props) {
super(props);
this.state = {
isTileDetailOpened:false
};
this.showTileDetails=this.showTileDetails.bind(this);
}
showTileDetails(networkName){
this.setState({isTileDetailOpened:!this.state.isTileDetailOpened});
alert(networkName);
}
render() {
const isTileDetailOpened = this.state.isTileDetailOpened;
return (
<li className={ !this.state.isTileDetailOpened ? "tile-item" : "tile-item-with-detail" }>
<div onClick={(e) => this.showTileDetails(this.props.objTile.network_name)}>
{this.props.objTile.network_name}
</div>
{this.state.isTileDetailOpened ? <TileDetail /> : <div/>}
</li>
);
}
}
export default Tile;
/* ---- Tile Detail component ---- */
import React, { Component } from 'react';
class TileDetail extends Component {
render() {
return (
<div className="tile-detail">
<p>TileDetails</p>
</div>
);
}
}
export default TileDetail;
As you can see, I am toggling the tile detail on click of tile click and its working fine.
I am rendering collecting of tiles on my page. And it works fine if I click on individual tile. But What I want if I am seeing the detail of one tile other tile details should be always hidden.
Could you please guide me on this. Completely new on React JS

You'll have to make use of the parent component of the Tile to achieve this. In the parent component have a state like currentlyTileDetailOpened. In showTileDetails call a method via props to set the value of this state(currentlyTileDetailOpened) to the id of the current Tile. Pass this state to the Tile component as a prop. In render method of Tile, check the prop instead of state and render it like
{this.props.currentlyOpenedTile === tile.id ? <TileDetail /> : null}

I just did this with a pagination control unit I made by storing the page info in Redux, and then making a special option in the second paginator so that it just used the Redux data instead of "double performing" the same work (and thus also not going out of sync with the first paginator).
It's like this (you could do the same with setting the parent's state (ie: first common ancestor):
<Pagination
pageSize={25}
items={(!this.state.fuzzyMatches.length)
? this.props.businesses
: this.state.fuzzyMatches}
onChangePage={(pagerState) => this.handlePageChange(pagerState)}
activePage={this.props.activePage}
isEmpty={((this.state.search.length > 0) &&
(this.state.fuzzyMatches.length === 0))}
bottom // bottom = true
bottomData={this.props.pagination} // get data from here
/>
Now, inside the paginator, instead of the normal logic, it does this:
render() {
const pager = (this.props.bottom)
? this.props.bottomData
: this.state.pager
The bottom = true prop is actually just to handle a slight CSS difference, but you can see, the first paginator creates the details, and the second one simply uses those instead of duplicating. I have told the second component to skip all its internal work and just use the data that is passed in.

Related

In React, what's the preferred design pattern for communicating data between a nested node and a sibling in a different branch?

I have a scenario where I'm a bit confused on how to approach. I feel like there's a lot of refactoring to be done if I were to communicate from the lowest level all the way back up to the parent. I'm very new to using React (a few days now) and I don't know if there's an easier solution to communicate data between components. I've read some sources that said child to parent to other child is best, but I haven't seen mentions of great-great-grandchildren to parent to a child. I've seen mentions about Context API, ComponentDidMount/Update, but I'm not sure what the best approach is.
I'm creating a data explorer on this webpage so we have the RootComponent, LeftPanel, and Graph component.
The LeftPanel holds an Explorer component, which will go through multiple levels of data. On the last level of data, if clicked, it should display a Graph component (through conditional rendering).
ex:
The Parent
export default class RootComponent extends React.Component {
render() {
return (
<div className="flex-row flex-grow">
<LeftPanel />
//Need to conditionally render this
<Graph />
</div>
)
}
}
LeftPanel is a container within the parent that will hold an explorer
export default class LeftPanel extends React.Component {
... (bunch of other logic)
render() {
return (
<Card className="flex-column default-vertical-spacing">
<div className="content">
<Explorer
..........>
</div>
</Card>
)
}
}
Explorer holds data as well as a "Pill" or button to click which will show further levels of data
export default class Explorer extends React.Component<IExplorerProps> {
... (bunch of logic)
render() {
if(!this.props.items || this.props.items.length == 0){
return <span>Loading</span>
}
return (
<ExpandablePill
............../>
)
}
}
ExpandablePill is data that can keep going downward
export default class ExpandablePill extends React.Component<ExpandablePillProps, ExpandablePillState> {
render() {
...
//Here I want to do: If this ExpandablePill was clicked AND it has no children, display a graph.
}
}
This is under the parent RootComponent but I need to know if an ExpandablePill with no levels left was clicked to display a graph (or make it known to the parent-RootComponent that I can display a graph now)
export default class Graph extends React.Component {
render() {
return (
<LineChart
...
</LineChart>
);
}
}
Any suggestions would be appreciated! I apologize if this is a duplicate.

Inserting a element into REACT child

I am rendering a chart with react, and I would like to add an element to the title of that chart. Unfortunately, I am using a shared chart component, and majorly modifying the component would be difficult to justify.
I have tried using refs; however, I'm running into difficulty figuring how to actually append a virtual dom element as a child element.
Specific Chart Class:
class ChartWithInfo extends React.Component {
constructor(props){
super(props);
this.chartWrapperElement = React.createRef();
}
componentDidMount() {
const infoInsertion = (
<div>
<IconButton/>
</div>
)
this.chartWrapperElement.current.insertBefore(infoInsertion, this.chartWrapperElement.current.firstChild);
}
render() {
return (
<GenericChart
variables={this.props.variables}
ref={this.chartWrapperElement}
/>
);
}
}
Generic Chart Class
export default class EmbeddedChart extends PureComponent {
// Random methods //
render() {
return (
<div ref={this.props.ref} id={'chartDiv'}>
Chart
</div>
);
}
}
The expected result would essentially be:
<div id='chartDiv'>
<IconButton/>
</div>
What is the most react way to do this? Am I missing something with refs?
React is meant for components and if you follow the separation of concern and component architecture. You can segregate all your components and individual reusable components. Create your button component separately and import anywhere else. Bind your events and business logic respectively.

React hiding components without react router

I have 4 components. The app component which has everything, a menu component which has two link options: General and reviews, and the two components I’m trying to hide/show: General component and reviews component.
How can I make it so that the menu component originally shows the General component and if I click on the reviews link, it would hide the General component and show the reviews components. Then if you click on the General component link, it would hide the reviews component and show the General component. I don’t need to use router since I’m not changing links, I just need to hide and show components.
I have the idea of adding an active state on the app state but not sure how it would work.
In your App component, have a state item currentView, which would hold the page you would like to display, Either by using an if and else, or just hiding them when they are not the option selected
class App extends React.Component {
constructor() {
this.state = {
currentView: 'general' // general|reviews|etc...
}
this.changeView = this.changeView.bind(this);
}
changeView(viewName) {
this.setState({
currentView: viewName
});
}
render() {
return (
<div className="menu">
{this.state.currentView !== 'general' && (
<a onClick={() => this.changeView('general')}>
General
</a>
)}
{this.state.currentView !== 'reviews' && (
<a onClick={() => this.changeView('reviews')}>
Reviews
</a>
)}
</div>
);
}
}

Understanding React Higher-Order Components

Can someone please explain Higher-order components in React. I have read and re-read the documentation but cannot seem to get a better understanding. According to the documentation, HOCs help remove duplication by creating a primary function that returns a react component, by passing arguments to that function.
I have a few questions on that.
If HOCs create a new enhanced component, can it be possible not to pass in any component as argument at all?
In an example such as this, which is the higher order component, the Button or the EnhancedButton.
I tried creating one HOC like this:
// createSetup.js
import React from 'react';
export default function createSetup(options) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.testFunction = this.testFunction.bind(this);
}
testFunction() {
console.log("This is a test function");
}
render() {
return <p>{options.name}</p>
}
}
}
// main.js
import React from 'react';
import {render} from 'react-dom';
import createSetup from './createSetup';
render((<div>{() => createSetup({name: 'name'})}</div>),
document.getElementById('root'););
Running this does not show the HOC, only the div
Can anyone help out with a better example than the ones given?
A HOC is a function that takes a Component as one of its parameters and enhances that component in some way.
If HOCs create a new enhanced component, can it be possible not to pass in any component as argument at all?
Nope, then it wouldn't be a HOC, because one of the conditions is that they take a component as one of the arguments and they return a new Component that has some added functionality.
In an example such as this, which is the higher order component, the Button or the EnhancedButton.
EnhanceButton is the HOC and FinalButton is the enhanced component.
I tried creating one HOC like this: ... Running this does not show the HOC, only the div
That's because your createSetup function is not a HOC... It's a function that returns a component, yes, but it does not take a component as an argument in order to enhance it.
Let's see an example of a basic HOC:
const renderWhen = (condition, Component) =>
props => condition(props)
? <Component {...props} />
: null
);
And you could use it like this:
const EnhancedLink = renderWhen(({invisible}) => !invisible, 'a');
Now your EnhancedLink will be like a a component but if you pass the property invisible set to true it won't render... So we have enhanced the default behaviour of the a component and you could do that with any other component.
In many cases HOC functions are curried and the Component arg goes last... Like this:
const renderWhen = condition => Component =>
props => condition(props)
? <Component {...props} />
: null
);
Like the connect function of react-redux... That makes composition easier. Have a look at recompose.
In short, If you assume functions are analogues to Components, Closure is analogous to HOC.
Try your createSetup.js with:
const createSetup = options => <p>{options.name}</p>;
and your main.js
const comp = createSetup({ name: 'name' });
render((<div>{comp}</div>),
document.getElementById('root'));
A higher-order component (HOC) is an advanced technique in React for reusing component logic. Concretely, a higher-order component is a function that takes a component and returns a new component.
A HOC is a pure function with zero side-effects.
Example: CONDITIONALLY RENDER COMPONENTS
Suppose we have a component that needs to be rendered only when a user is authenticated — it is a protected component. We can create a HOC named WithAuth() to wrap that protected component, and then do a check in the HOC that will render only that particular component if the user has been authenticated.
A basic withAuth() HOC, according to the example above, can be written as follows:
// withAuth.js
import React from "react";
export function withAuth(Component) {
return class AuthenticatedComponent extends React.Component {
isAuthenticated() {
return this.props.isAuthenticated;
}
/**
* Render
*/
render() {
const loginErrorMessage = (
<div>
Please login in order to view this part of the application.
</div>
);
return (
<div>
{ this.isAuthenticated === true ? <Component {...this.props} /> : loginErrorMessage }
</div>
);
}
};
}
export default withAuth;
The code above is a HOC named withAuth. It basically takes a component and returns a new component, named AuthenticatedComponent, that checks whether the user is authenticated. If the user is not authenticated, it returns the loginErrorMessage component; if the user is authenticated, it returns the wrapped component.
Note: this.props.isAuthenticated has to be set from your application’s
logic. (Or else use react-redux to retrieve it from the global state.)
To make use of our HOC in a protected component, we’d use it like so:
// MyProtectedComponent.js
import React from "react";
import {withAuth} from "./withAuth.js";
export class MyProectedComponent extends React.Component {
/**
* Render
*/
render() {
return (
<div>
This is only viewable by authenticated users.
</div>
);
}
}
// Now wrap MyPrivateComponent with the requireAuthentication function
export default withAuth(MyPrivateComponent);
Here, we create a component that is viewable only by users who are authenticated. We wrap that component in our withAuth HOC to protect the component from users who are not authenticated.
Source
// HIGHER ORDER COMPOENTS IN REACT
// Higher order components are JavaScript functions used for adding
// additional functionalities to the existing component.
// file 1: hoc.js (will write our higher order component logic) -- code start -->
const messageCheckHOC = (OriginalComponent) => {
// OriginalComponent is component passed to HOC
const NewComponent = (props) => {
// business logic of HOC
if (!props.isAllowedToView) {
return <b> Not Allowed To View The MSG </b>;
}
// here we can pass the props to component
return <OriginalComponent {...props} />;
};
// returning new Component with updated Props and UI
return NewComponent;
};
export default messageCheckHOC;
// file 1: hoc.js -- code end -->
// file 2: message.js -- code start -->
// this is the basic component we are wrapping with HOC
// to check the permission isAllowedToView msg if not display fallback UI
import messageCheckHOC from "./hoc";
const MSG = ({ name, msg }) => {
return (
<h3>
{name} - {msg}
</h3>
);
};
export default messageCheckHOC(MSG);
// file 2: message.js -- code end -->
// file 3 : App.js -- code start --->
import MSG from "./message.js";
export default function App() {
return (
<div className="App">
<h3>HOC COMPONENTS </h3>
<MSG name="Mac" msg="Heyy !!! " isAllowedToView={true} />
<MSG name="Robin" msg="Hello ! " isAllowedToView={true} />
<MSG name="Eyann" msg="How are you" isAllowedToView={false} />
</div>
);
}
// file 3 : App.js -- code end --->

How to alter the state of a parent based on passed ref in React?

I am attempting to implement an app-wide global header that displays above each route's content.
I envisioned a parent component that controls both the header and a set of children. The header's content is based on which child is currently displayed.
I thought I could accomplish this by sending refs to each child route that would activate when that specific route is rendered. The ref would grab values on the child and set the state of the parent. The header is a child of the parent and takes those values as a prop, thus updating.
Unfortunately, updating the parent component's state when a ref passed to the child is fired causes the ref to be called thousands of times.
SceneContainer
-> Header (Controlled component, changes when scene changes)
-> Switch (Renders a single route from children, react-router)
-> Scene -> Route (Has ref, renders when path matches, should change content of header)
-> Scene -> Route (same as above)
-> etc
I would like to be able to set a field like this in each "Scene file" that alters the state of the Header. Similar to how React Native handles navigation.
This is a header that will be across multiple pages, corresponding information to whatever scene it is displaying.
The code for my SceneContainer component is as follows:
class SceneContainer extends Component {
constructor(props) {
super(props)
this.state = {
title: "from scenecontainer",
subtitle: "sub from scenecontainer",
}
this.generateReference = this.generateReference.bind(this)
}
generateReference() {
return el => {
console.log("Firing reference")
if (el !== null) {
const header = el.wrappedInstance.header
this.setState(header)
}
}
}
render() {
let newChildren = React.Children.map(this.props.children, child =>
React.cloneElement(child, {
generateReference: this.generateReference,
})
)
return (
<div>
<Header title={this.state.title} subtitle={this.state.subtitle} />
{newChildren}
</div>
)
}
}
export default SceneContainer
"Firing Reference" is shown thousands of times in the console

Categories

Resources