Can we render the extended component and wrap the render? - javascript

Actually I wanted to create a custom component for rechart using the component from the rechart library. When I try to render the data i am unable to see the output. Let me show you some example.
import { LineChart as ReLineChart} from 'recharts';
export class MyLine extends ReLine {
static defaultProps = {
...ReLine.defaultProps,
// Customized props
strokeWidth: 2,
dot: false,
};
}
When I use <MyLine /> it is rendering properly and I can see the output in the browser a Line drawn and I can also pass the props required in the jsx. But when I use a render function in the class component the line is not getting rendered. Can anyone provide me a solution to get it rendered
export class MyLine extends ReLine {
static defaultProps = {
...ReLine.defaultProps,
// Customized props
strokeWidth: 2,
dot: false,
};
render(){
return <div>{}</div>
}
Provide me a solution.

Related

styling SVG paths in React

I am new to React and I'm trying to add an svg image to my component and add dynamic styles to each of its path. However, I am having issues in doing so. I'm using create-react-app
I imported my svg as a React component
import { ReactComponent as SVGimg} from './img/svg-img.svg';
and used it on my render. I was hoping to add styles to all paths by using a for...of loop which I worked on plain HTML/CSS/JS
render() {
const svgImg = document.querySelectorAll('svg path')
for (let path of svgImg) {
path.classList.add('some-class');
}
return(
<div>
<SVGimg/>
</div>
);
}
I also want to mention that the SVG has a lot of paths
You could add a class to the svg say: <svg><path class='pathClass'></svg>
Then in your React component you can get the path by calling: const path = document.getElementsByClassName('pathClass');
And add the class like so: path.classList.add('some-class');
UPDATE
Since you are using a class component you could call the classList.add() method inside the componentDidMont() native React method like so:
class YourComponent extends Component {
constructor(props) {
super(props);
this.svgRef = React.createRef();
}
componentDidMount() {
this.svgRef.current.classList.add('some-class');
}
render() {
return(
<div>
<SVGimg ref={this.svgRef} />
</div>
);
}
}
This will be triggered immediately after the render method finishes.

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.

Onepage with Gatsby JS and Contentful, how to import my first simple string

I am trying to useStatic Query and GraphQL to get a simple title from
Contentful, pass it to state and then show in the render. I cant make it work. I am attaching an image showing my current setup and errors.
Possible problems: 1. the query returns an array, and I need to change it into a string, or access 0 element, the first one, because my content type is just one, as it is a onepage.
Placing of the query in the component, I am not sure if it can be in the constructor of an component
For comparison: in the screen from my file you can see a variable name showing Josh Perez, when I uncomment it and add it to this.state = { dataTest: name}, then in RENDER: this.state.dataTest returns the name Josh Perez well, so passing a variable to state works, but passing a string from graphql query is not possible for me...
I have a limitation which is that I need to create my page component with a class, because of the fact that in the Component did mount I am placing some JQuery, which works well for me.
THIS IS MY TEST CODE
1. In Constructor
class IndexPage extends React.Component {
constructor(props) {
super(props);
// this.state = { data: null };
const name = 'Josh Perez';
this.state = { dataTest: name };
}
In render
{this.state.dataTest}
This works, the variable name is passed to state and shown in render.
However, I want to show in this way a simple text string from Contentful. So I am trying code like this (error message is shown in the screens):
class IndexPage extends React.Component {
constructor(props) {
super(props);
// this.state = { data: null };
//const name = 'Josh Perez';
const data = useStaticQuery(graphql`
query {
allContentfulHomepage (limit: 1) {
edges {
node {
section1Title
}
}
}
}
`)
this.state = { dataTest: data };
It turns out, that the below suggested solution works. I am putting below
my attempt at callingfurther content. It does not work. It displays the following error "Cannot read property 'map' of undefined". I would be very grateful for a suggestion how to improve it, how to make it work.
export default class Test extends Component {
state = {
dataTest: this.props.data.test.edges.map(({ node: test }) =>
test.section1Title),
dataTest2: this.props.data.test.edges.map(({ node: test }) =>
test.section2Lead),
dataTest3: this.props.data.test.edges.map(({ node: test }) =>
test.section1Text.json)
}
render() {
return <div>
<h1>{this.state.dataTest}</h1>
<h1>{this.state.dataTest2}</h1>
{documentToReactComponents(this.state.dataTest3)}
</div>
}
}
export const query = graphql`
{
test:allContentfulHomepage(limit: 1) {
edges {
node {
section1Title
section2Lead
section1Text {
json
}
}
}
}
}
`
If you're writing a page component as a class, you don't need to use the UseStaticQuery, you can use the simple PageQuery for this purpose.
To loop through arrays, the map() method works as well.
UPDATE
import React, { Component } from 'react';
import { graphql } from 'gatsby';
export default class Test extends Component {
render() {
const { edges } = this.props.data.test;
return (
<div>
{edges.map(({ node: itemFromContentful }) => (
<h1>{itemFromContentful.section1Title}</h1>
<h1>{itemFromContentful.section2Lead}</h1>
{documentToReactComponents(section1Text.json)}
))}
</div>
);
}
}
export const query = graphql`
{
test:allContentfulHomePage(limit: 1) {
edges {
node {
section1Title
}
}
}
}
`
Whats happening:
The GraphQL query you're using is bringing the data you want from the Contentful;
The React Stateful Component (class Test) is receiving all the data available from the query as a prop;
We're accessing this data on the render() method using the destructing assignment;
we're accessing the data nodes through the map method (the one I suggested you to take a look;
The curly braces into the JSX allows you to use JS to manipulate what you want - In this case, to render the information.

Blacklist React components

Is there a way to define a function to hook before each component in my app is mounted?
The idea is that if a component is blacklisted it doesn't mount at all.
The solution must leave the components unmodified for backward compatibility and should run in production (so rewire and other testing tools are probably off the table but open to suggestions :) )
Example
//something like this...
ReactDOM.beforeEachComponentMount( (component, action) => {
if(isBlacklisted(component)){
action.cancelMountComponent();
}
}
Could you write a simple Babel plugin that transforms blacklisted components to a noop functional component () => {} at compile time?
You could wrap the required components inside a higher order component that checks whether the component is blacklisted or not.
for example :
class YourComponent extends Component {
constructor(props){
super(props);
}
render(){
return(
// your component goes here ..
);
}
}
export default WithPermission(YourComponent);
check if the component needs to be rendered or not inside the HOC WithPermission.
function withPermission(YourComponent) {
class WithPermission extends React.Component {
constructor(props) {
super(props);
}
// you can check the props inside ComponentDidMount and set a flag if
// the component satisfies the criteria for rendering.
render() {
const {blacklistedComponents,...rest} = this.props;
if(!blackListedComponents){
return <YourComponent {...rest} />
}
else{
return null;
}
}
}
}
There is no such functionality out of box.
You may shim React rendering cycle, I mean shim React.createElement method and validate component before it is added to VDOM
All JSX is processed through React.createElement
e.g. at the start of app add
let React = require('react');
let originalCreateElement = React.createElement;
React.createElement = function() {
let componentConstructorOrStringTagName = arguments[0];
if (isBlacklisted(componentConstructorOrStringTagName)) {
return null;
}
return originalCreateElement.apply(this, arguments);
}
The best idea I can think of is to "shim" react and Component
if you are using webpack you can use this:
https://webpack.js.org/guides/shimming/
in the bottom line that means instead of importing react you will import your own class of react.
In your new class you could extend React Component and place a check on the render function or something similar.
You could implement a custom ESLint rule and catch this as soon as a dev tries to use a blacklisted components. The id-blacklist rule is similar to what you want, but at the identifier level. The source code looks simple. Maybe you can adapt it to disallow more then just identifiers.
Consider the following solution:
Let there be a file where you declare which components are blacklisted:
let blacklist = [{
name: 'secretComponent',
invoke: (props)=> {
return <SecretComponent ...props />
},
isBlacklisted: true
},{
name: 'home',
invoke: (props)=> {
return <HomeComponent ...props />
},
isBlacklisted: false
},{
name: 'login',
invoke: (props)=> {
return <LoginComponent ...props />
},
isBlacklisted: false
}];
Define a Higher Order Component like below:
function renderIfNotBlacklisted(name) {
let component = blacklist.map(x=> x.name == name); //blacklist from above
if (!component.isBlacklisted){
return component.invoke();
} //else can be handled as you will
//You can keep a default component to render or send empty values
}
Call this component in the render function wherever you want this feature to work. This way you have a centralized location to managed blacklisted components (blacklist.json can be in the root of react project or fetched from API on first run)

Can AnyChart the HTML version be used with React?

I would like to use AnyChart library with my current React, Redux stack. Is there a way to wrap AnyCharts in something like FauxDom. Would be nice if you can provide me with a sample code snippet or direction to a library that does that.
As for the client side React rendering, it is surely possible to use AnyChart wrapped in a React component.
You could write a wrapping AnyChart component accepting the data array and title as props in this way (example of a pie chart wrapper):
import React, { Component } from 'react';
class AnyChart extends Component {
constructor(props) {
super(props);
}
// Important, otherwise the re-render
// will destroy your chart
shouldComponentUpdate() {
return false;
}
componentDidMount() {
// Get data from the props
let data = this.props.data;
let title = this.props.title;
// Let's draw the chart
anychart.onDocumentReady(function() {
let chart = anychart.pie(data);
chart.container('chart');
chart.title(title);
chart.draw();
});
}
render() {
return (
<div id="chart" style={{height: '400px'}}/>
);
}
}
export default AnyChart;
You can then use this component from another react component.
For example, from a functional component:
import React from 'react';
import AnyChart from './AnyChart';
const AnyChartTest = (props) => {
const data = [
['React', 5200],
['ES6', 2820],
['Redux', 2650],
['Redux Ducks', 670]
];
return (
<div>
<h1>AnyChart Test</h1>
<AnyChart data={data} title="Technology Adoption" />
</div>
);
};
export default AnyChartTest;
This works well if you don't need to have the chart dynamically updated with new data from the props. If that is the case you should add a ComponentWillReceiveProps handler in the AnyChart wrapper component, where you should pass new data from the props to the chart and force a redraw.
Stephen Grider made a very good video on third party components integration:
https://www.youtube.com/watch?v=GWVjMHDKSfU
I hope I helped you, at least for client-side rendering.
Matteo Frana

Categories

Resources