So I have this JSX element that I am attempting to render in the Class component. It is essentially a visual provided by D3's React library. However, I am receiving this error upon attempting to render the D3 visual:
Unhandled Rejection (Error): Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Below are some relevant code snippets of where the error is occurring:
The builder function to pass all the necessary props to the D3 library
const buildModelVisual = (dataPoints) => {
console.log("category: " + dataPoints[0].category)
console.log("range: " + dataPoints[0].range)
console.log("frequency: " + dataPoints[0].frequency)
dataPoints[0].frequency.forEach(f =>
console.log("f: " + f)
)
const width = 960,
height = 500,
margins = {top: 50, right: 50, bottom: 50, left: 50},
id = "model-visual",
title = "NaiveBayes Model Visual",
svgClassName = "model-visual-class",
titleClassName = "model-visual-title-class",
legendClassName = "model-legend",
showLegend = true,
showXAxis = true,
showYAxis = true,
showXGrid = false,
showYGrid = false,
ranges = [
...dataPoints[0].range
],
frequencies = [
...dataPoints[0].frequency
],
x = () => {
return ranges.forEach(r => {
return r;
})
},
xOrient = 'bottom',
xTickOrient = 'bottom'
const xDomain = dataPoints[0].range.forEach(r => {
return {
category: dataPoints[0].category, range: r
}
}),
xRangeRoundBands = {interval: [0, width - margins.left - margins.right], padding: 0.1},
xScale = 'ordinal',
xAxisClassName = 'x-axis',
xLabel = dataPoints[0].category,
xLabelPosition = 'bottom',
xTickPadding = 3,
xInnerTickSize = 6,
xOuterTickSize = 6,
y = () => {
return frequencies.forEach(freqInRange => {
return freqInRange.forEach(f => {
return f;
});
})
},
yOrient = 'left',
yTickOrient = 'left',
yRange = [height - margins.top - margins.bottom, 0]
const yDomain = [0, d3.max(
dataPoints[0].frequency,
(f) => {
return f.value
}
)],
yScale = 'linear',
yAxisClassName = 'y-axis',
yLabel = "Population",
yTickFormat = d3.format(".2s"),
yLabelPosition = 'left',
yTickPadding = 4,
yInnerTickSize = 6,
yOuterTickSize = 6
return (
<Chart
title={title}
id={id}
width={width}
height={height}
>
<BarStackChart
title= {title}
data= {ranges}
width= {width}
height= {height}
id= {id}
margins= {margins}
svgClassName= {svgClassName}
titleClassName= {titleClassName}
yAxisClassName= {yAxisClassName}
xAxisClassName= {xAxisClassName}
legendClassName= {legendClassName}
categoricalColors= {d3.scaleOrdinal(d3.schemeCategory10)}
chartSeries = {ranges}
showLegend= {showLegend}
showXAxis= {showXAxis}
showYAxis= {showYAxis}
x= {x}
showXGrid= {showXGrid}
xDomain= {xDomain}
xRangeRoundBands= {xRangeRoundBands}
xScale= {xScale}
xOrient= {xOrient}
xTickOrient= {xTickOrient}
xTickPadding = {xTickPadding}
xInnerTickSize = {xInnerTickSize}
xOuterTickSize = {xOuterTickSize}
xLabel = {xLabel}
xLabelPosition = {xLabelPosition}
y= {y}
showYGrid= {showYGrid}
yOrient= {yOrient}
yRange= {yRange}
yDomain= {yDomain}
yScale= {yScale}
yTickOrient= {yTickOrient}
yTickPadding = {yTickPadding}
yInnerTickSize = {yInnerTickSize}
yOuterTickSize = {yOuterTickSize}
yTickFormat= {yTickFormat}
yLabel = {yLabel}
yLabelPosition = {yLabelPosition}
/>
</Chart>
)
}
The HO class component that is rendering the graph and the interface
constructor(props) {
super(props);
this.ref = React.createRef();
this.state = {
input: "",
output: [],
visual: null
}
}
REST API call (within the default class Component) that sets the data for the BarStackChart
callGetModel = () => {
let getModelRanges = getCall(ApiURL.get_model_ranges);
let getModelFrequencies = getCall(ApiURL.get_model_frequencies);
Promise.all([getModelRanges, getModelFrequencies]).then(data => {
this.setState({output: data})
const dataPoints = [];
for (let value in JSON.parse(data[0].toString())) {
dataPoints.push({
category: value,
range: JSON.parse(data[0].toString())[value],
frequency: JSON.parse(data[1].toString())[value]
})
}
console.log(dataPoints)
const ModelVisual = buildModelVisual(dataPoints)
this.setState({ visual: ModelVisual }) // returns JSX element
console.log(this.state.visual)
});
}
The render method for the class Component
render() {
return <div>
<h3>Welcome to Naive Bayes Java!</h3>
<p>The REST API for this project is hosted at</p>
<a style={{'display':'block', 'paddingBottom':'1.5em', 'color':'rgb(0, 150, 196)'}} href="https://naivebayesjava.herokuapp.com/swagger-ui.html#/">https://naivebayesjava.herokuapp.com/</a>
<button style={{'display':'inline', 'background':'rgb(32, 32, 32)', 'color':'rgb(190, 190, 190)'}} onClick={this.callListOfFiles}>
Get List of Valid Files
</button>
<button style={{'background':'rgb(32, 32, 32)', 'color':'rgb(190, 190, 190)'}} onClick={this.callGetModel}>
Get Model
</button>
<button style={{'background':'rgb(32, 32, 32)', 'color':'rgb(190, 190, 190)'}} onClick={this.callGetModelAccuracy}>
Get Model Accuracy
</button>
<div style={{'margin':'auto', 'display':'block'}}>
<input style={{'background':'rgb(32, 32, 32)', 'color':'rgb(190, 190, 190)'}} type='text' value={this.state.input} onChange={this.handleChange}/>
<button style={{'background':'rgb(32, 32, 32)', 'color':'rgb(190, 190, 190)'}} onClick={this.callSetData}>
Set Training Data File
</button>
</div>
{/* <button onClick={this.callGetTrainingData}>
Get Training Data Arff Files
</button> */}
<div style={{'padding-top':'0.5em'}} ref={this.ref}></div>
<output type='textBox' style={{ 'padding':'1.25em', 'display':'block', 'Height': '30px', 'Width': '300px' }}>
{ Object.keys(this.state.output).map(key => {
return this.state.output[key]
}) }
</output>
{ this.state.visual }
</div>
}
There most definitely is a better way to implement this besides setting a "this.state.visual" JSX Element and calling that in the render method, although as I am really new to both React (started learning about a month ago) and JS (started about 3 months ago) I don't quite know all the common practices with either; just the general theory behind how they work.
This interface and my portfolio are hosted at joshuabaroni.github.io . The interface i am attempting to improve is the NaiveBayes Project Interface under the "Projects" section
Any recommendations would be appreciated! the whole JS file is available upon request.
You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
You aren't exporting your classes/functions as it is required.
Exporting without default means it's a "named export". You can have multiple named exports in a single file. So if you do this,
class Template {}
class AnotherTemplate {}
export { Template, AnotherTemplate } // named export
then you have to import these exports using their exact names. So to use these components in another file you'd have to do,
import {Template, AnotherTemplate} from './components/templates'
Alternatively if you export as the default export like this,
export default class Template {}
Then in another file you import the default export without using the {}, like this,
import Template from './components/templates'
There can only be one default export per file. In React it's a convention to export one component from a file, and to export it is as the default export.
You're free to rename the default export as you import it,
import TheTemplate from './components/templates'
And you can import default and named exports at the same time,
import Template,{AnotherTemplate} from './components/templates'