So, the following implementation works just fine to read JSON data and turn it into rendered components - until I try to add the children. Then, it spits out an error.
function:
const catalogRenderer = (config) => {
if (typeof KeysToComponentMap[config.component] !== "undefined") {
return React.createElement(
KeysToComponentMap[config.component],
{
key: config.key,
title: config.title
},
{
config.children && config.children.map(c => catalogRenderer(c))
}
);
}
}
error:
app.js:134 Uncaught Error: Module build failed (from ./node_modules/babel-loader/lib/index.js)
"...Scripts/CatalogRenderer.js: Unexpected token, expected "," (25:14)"
console:
},
24 | {
> 25 | config.children && config.children.map(c => catalogRenderer(c))
| ^
26 | }
27 | );
28 | }
I'm using react as part of an electron application, it's a long story about all the moving parts, but everything else so far has worked just fine. In the editor, if I move to the preceding { from that mysteriously disliked . on line 25, it's highlighting the period as if this should somehow close the bracket.
Is there something I'm not understanding about the syntax here? The same thing happens if I attempt to just map and render the children like so:
{
config.children.map(c => catalogRenderer(c))
}
I've tried enclosing the whole statement in brackets, curly braces, parentheses--no matter what I do, babel seems to expect a comma, but giving it a comma obviously doesn't help. Any idea what I'm doing wrong?
eta: This is the JSON object I'm attempting to render from:
const catConfig = {
catalog: [
{
component: 'pen',
title: `B.C. Palmer`,
key: `B.C.PalmerPen`,
children: `A child string`
},
{
component: 'content',
key: `B.C.PalmerWorldList`,
children: [
{
component: 'world',
title: `Rismere`,
key: `RismereWorld`
},
{
component: 'content',
key: `RismereSeries`,
children: [
{
component: 'series',
title: `The Eidolon War`,
key: `TheEidolonWarSeries`
},
{
component: 'content',
key: `TheEidolonWarBooks`,
children: [
{
component: 'book',
title: `Magic's Heart`,
key: `MagicsHeartBook`
},
{
component: 'book',
title: `Magic's Fury`,
key: `MagicsFuryBook`
},
{
component: 'book',
title: `Magic's Grace`,
key: `MagicsGraceBook`
}
]
}
]
}
]
},
{
component: 'pen',
title: `Simon Strange`,
key: `SimonStrangePen`
}
]
}
This JSON will be generated via a database call, and written each time the database is updated, and update the state of the 'catalog' component.
So, for example, the second object in the catalog array above is a container which, when the first 'pen' component is clicked, becomes visible and shows a list of 'world' components (in this case, just the one.) However, the function only successfully renders any 'parent' components--if I take out the curly braces at lines 24 and 26, it simply skips them but doesn't error.
The components are composed of button elements and a div (content). The buttons will likely become Link element when I get this working, but the original version was written in vanilla javascript, I haven't implemented routing with the catalog yet. So, the pen component for example:
import React from 'react'
export default penButton => {
return(
<button className="catalogItem pen">
<img src="src/icons/catPenName.png" className="catalogIcon"/>
<p className="contentLabel">{penButton.title}</p>
</button>
)
}
Is a top level component, and gets rendered just fine. It's next sibling (and the next sibling of any button except a book) is content:
import React from 'react'
export default contentList => {
return(
<div className="contentList">
</div>
)
}
contentList is just a div with the contentList class, which handles visibility and animation. Should I have a place for the "children" key in JSON to populate the children of content?
When you want to render multipile children elemen'ts into your react component, you need to pass each child as a seperate parameter.
See this answer as an example:
how to render multiple children without JSX
so your solution should be to use spread syntax.
here is an example:
const catalogRenderer = (config) => {
if (typeof KeysToComponentMap[config.component] !== "undefined") {
let childs = [];
if (config.children) {
childs = config.children.map(c => catalogRenderer(c));
}
return React.createElement(
KeysToComponentMap[config.component],
{
key: config.key,
title: config.title
},
...childs
);
}
}
Well, that was simple and a little silly. I updated the content component to:
import React from 'react'
export default contentList => {
return(
<div className="contentList">
{contentList.children} <---- added
</div>
)
}
Content didn't have a place to put children. Obviously.
Related
I'm trying to use an array of dictionaries in python as arguement to a custom dash component and use it as array of objects
in python :
audioList_py = [
{
"name": "random",
"singer": 'waveGAN\'s music',
"cover":
'link_1.jpg',
"musicSrc":
'link_1.mp3',
},
{
"name": "random",
"singer": 'waveGAN\'s music',
"cover":
'link_2.jpg',
"musicSrc":
'link_2.mp3',
},
... etc
]
in Javascript:
audioList1_js = [
{
name: "random",
singer: 'waveGAN\'s music',
cover:'link_1.jpg',
musicSrc: 'link_1.mp3',
},
{
name: "random",
singer: 'waveGAN\'s music',
cover: 'link_2.jpg',
musicSrc: 'link_2.mp3',
},
... etc
]
Here is snippet of javascript code of the dash custom component:
export default class MusicComponent extends Component {
render() {
const {id, audioLists} = this.props;
return (
<div>
<h1>{id}</h1>
<ReactJkMusicPlayer audioLists={audio_list}/>,
</div>
);
}
}
MusicComponent.defaultProps = {};
MusicComponent.propTypes = {
/**
* The ID used to identify this component in Dash callbacks.
*/
audios: PropTypes.array,
id: PropTypes.string,
};
And using the generated component in python:
app = dash.Dash(__name__)
app.layout = html.Div([
music_component.MusicComponent(audios=audioList_py),
html.Div(id='output'),
... etc
])
But I got :
TypeError: The `music_component.MusicComponent` component (version 0.0.1) received an unexpected keyword argument: `audios`Allowed arguments: id
What I am doing wrong ?
Any help or advice will be appreciated, Thanks a lot.
Make sure you run npm run build after you make a change to your custom React component. With those proptypes you shouldn't get that error. If I remove the audios proptype I can reproduce that error.
Besides that you pass a value to the audios property:
music_component.MusicComponent(audios=audioList_py)
but you try to retrieve audioLists from props:
const {id, audioLists} = this.props;
Change this to:
const {id, audios} = this.props;
Demo
export default class MusicComponent extends Component {
render() {
const {id, audios} = this.props;
return (
<div>
<h1>{id}</h1>
<ReactJkMusicPlayer audioLists={audios} />
</div>
);
}
}
MusicComponent.defaultProps = {};
MusicComponent.propTypes = {
/**
* The ID used to identify this component in Dash callbacks.
*/
id: PropTypes.string,
audios: PropTypes.array,
};
Issue fixed, I should run : npm run build:backends to generate the Python, R and Julia class files for the components, but instead I was executing npm run build:js and this command just generate the JavaScript bundle (which didn't know about the new props).
And set the audios property in the component to be like so:
MusicComponent.defaultProps = {audios: audioList1};
MusicComponent.propTypes = {
id: PropTypes.string,
audios: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.string)).isRequired
};
I am having an issue with React creating a circular structure in an object I am passing as a prop. Basically, I am using react-bootstrap-table2 to render a table of data. This component requires two arrays of objects as props: columns and data. I am creating and passing these props, but for some reason they contain a circular structure. At some point, JSON.stringify is called and TypeError: Converting circular structure to JSON is thrown. The example below shows the problem occurring in the columns prop. I create a hard-coded array and one dynamically to showcase the difference. this.props.cols = ["2017","2018","2019"]
const columns1 = [
{
dataField: 'dataTopic',
text: null
},
{
dataField: '2017',
text:
<div>
<div>2016 – 2017</div>
</div>
},
{
dataField: '2018',
text:
<div>
<div>2017 – 2018</div>
</div>
},
{
dataField: '2019',
text:
<div>
<div>2018 – 2019</div>
</div>
}
];
class MyTable extends React.Component {
static propTypes = {
cols: PropTypes.array
};
constructor(props){
super(props);
this.getColumns = this.getColumns.bind(this);
this.getRows = this.getRows.bind(this);
}
getColumns() {
const columns = [
{
dataField: 'dataTopic',
text: null
}
];
this.props.cols.forEach((year) => {
columns.push(
{
dataField: year,
text:
<div>
<div>{year - 1} – {year}</div>
</div>
}
);
});
return columns;
}
getRows(){
//Do stuff
}
render() {
console.log(columns1);
console.log(this.getColumns());
return (
<div>
<BootstrapTable keyField='dataTopic' columns={this.getColumns()} data={this.getRows()} />
</div>
);
}
}
export default MyTable;
I get the following output to the console:
For some reason, everything is the same except for the _owner attribute in the JSX element. If I expand _owner, I find where my recursion is occurring:
I understand that _owner is used to track the parent of a React component, but I don't understand why it is null in columns1 and not in the object returned by getColumns(). Could someone please explain to me why this is?
The _owner is null for columns1 because this array has been declared outside of the MyTable component.
I'm just playing around with some component instance rendering within a Vue application and I was wondering, when pushing components to an array - how do we then access the data() from that given instance of the component?
So say I have something like this in App.vue (the "Grandfather" of all of my components). I have managed to push instances of both the CodeBlock and QuoteBlock components to the pageBlocks array (i.e., my front end app appends the component where I want it to be). Here is a snippet of my App.vue file:
components: {
CodeBlock,
QuoteBlock
},
data () {
return {
pageBlocks: []
}
},
methods: {
addPageBlock (componentNomen) {
this.pageBlocks.push({ componentName: componentNomen })
},
saveDraftPage () {
for (let pageBlock of this.pageBlocks) {
console.log(pageBlock.data)
}
}
}
And here is an example of my CodeBlock data (the Quote block is "modelled" almost like for like except for a few variable name changes to distinguish it inside the component):
export default {
name: 'CodeBlock',
props: [ 'type' ],
computed: {},
data () {
return {
debug: true,
codeBlock: null,
codeBlockRows: [{
'id': 1,
'text': '$ click to edit this code block'
}],
}
},
}
I've stripped out most of this component to keep things simple.
So, my question is, if the pageBlocks array in App.vue contains instances of the above exported component...how do I access the data within?
In my naiavity I thought it would be as simple as something like this:
for (let pageBlock of this.pageBlocks) {
console.log(pageBlock.data);
}
But, alas, no luck yet...any tips?
I installed the package react-treebeard
https://react.rocks/example/react-treebeard
To allow me to have a nice out of the box tree control for my React TS website which allowed searching etc. The online demo all works fine, however it is in ReactJS so there are some modifications I have had to make to translate it to TS syntax. However there are two problems
1) When I click on a node, it crashes out with
TypeError: Cannot read property 'cursor' of null
Content../src/Content.tsx.Content.onToggle
C:/src/investment/src/signoff/src/Content.tsx:94
91 | // } 92 | 93 | private onToggle(node: any, toggled: any) {
94 | const {cursor} = this.state; 95 | if (cursor) { cursor.active = false; } 96 | 97 | node.active = true;
and
2) The tabbing and indenting of the tree nodes is wrong - I assume I've not included some styling component somewhere but can't see what as I would have thought it was all in the node module package downloaded by npm install react-treebeard
The second one is less an issue as I can work through that, I just mentioned it in case something glaring jumps out. However the first one seems to be a basic problem with me not converting it to TS syntax correctly so hoping someone can spot the issue.
My component
import * as React from 'react';
import './App.css';
import {Treebeard} from 'react-treebeard';
import data from './components/treebeard/data';
interface IContentState {
data : any,
cursor : any
}
class Content extends React.Component<{},IContentState> {
constructor(props: any) {
super(props);
this.setState({cursor: null});
this.setState({data: data});
this.onToggle = this.onToggle.bind(this);
}
public render() {
const stateData = {
children: [
{
children: [
{ name: 'child1' },
{ name: 'child2' }
],
name: 'parent',
},
{
children: [],
loading: true,
name: 'loading parent',
},
{
children: [
{
children: [
{ name: 'nested child 1' },
{ name: 'nested child 2' }
],
name: 'nested parent',
}
],
name: 'parent',
}
],
name: 'root',
toggled: true,
};
return (
<div className="Center-content">
<div className="Tree-control">
<Treebeard data={stateData}
onToggle={this.onToggle}/>
</div>
</div>
);
}
private onToggle(node: any, toggled: any) {
const {cursor} = this.state;
if (cursor) { cursor.active = false; }
node.active = true;
if (node.children) { node.toggled = toggled; }
this.setState({cursor: node});
}
}
export default Content;
You setState the value of cursor: null, change it to true or false, null values always cause unexpected crashes. You should avoid putting null values on your components also I think you were trying to have this this.state = { cursor: null }
As far I can see you have no state
I am trying to set up a simple slate.js editor, with the following code:
import { Editor } from 'slate-react'
import { Value } from 'slate'
const initialValue = Value.fromJSON({
document: {
nodes: [
{
object: 'block',
type: 'paragraph',
nodes: [
{
object: 'text',
leaves: [
{
text: 'A line of text in a paragraph.',
},
],
},
],
},
],}, });
// Define our app...
class App extends React.Component {
// Set the initial value when the app is first constructed.
state = {
value: initialValue,
};
// On change, update the app's React state with the new editor value.
onChange = ({ value }) => {
this.setState({ value })
} // Render the editor.
render() {
return <Editor value={this.state.value} onChange={this.onChange} />
}
}
export default App
I simply copy pasted the code from the slate.js walkthorugh, but I get the following error:
./src/App.js
Syntax error: Unexpected character '' (34:0)
32 | this.setState({ value })
33 | }
> 34 |
| ^
35 | // Render the editor.
36 | render() {
37 | return <Editor value={this.state.value} onChange={this.onChange} />
It´s my first time both using react and slate, I just wanted to get a feel for it. I hope you can help me explaining whats wrong :)
I don't know if you've solved it already. But I just faced that problem, I think there's a leftover character from when you copied it from their documentation.
Try removing the whitespace completely between the end of the block and the comment and then add them to your liking again, it should work!