I have a React class with a render method that includes 1 or 3 child components.
If the user is logged out, only the first component should be rendered, and if the user is logged in (or when the user logs in) the second two should also be rendered.
render() {
return (
<div className="lists-wrapper {this.data.user}">
<List title="Featured" tiles={this.data.featured} />
{ this.data.user ?
<List title="Faves" tiles={this.data.faves} />
<List title="My Content" tiles={this.data.owned} />
}
</div>
);
}
However I get the following error:
Adjacent JSX elements must be wrapped in an enclosing tag (58:5)
I can find plenty of documentation and examples of conditionally including one component or another, but nothing which helps me in this use-case. Without the conditional, and adding all three of them works as expected.
You should wrap adjacent JSX elements in another element, like div.
Moreover, I will suggest to move the rendering logic to another function to make it clear.
/*
* A simple React component
*/
class List extends React.Component {
render() {
return <b>{this.props.title}</b>;
}
}
class Application extends React.Component {
renderSection(flag) {
if(flag) {
return (<div><List title="Faves"/>
<List title="My Content"/>
</div>);
}
}
render() {
return (
<div className="lists-wrapper">
<List title="Featured" />
{
this.renderSection(true)
}
</div>
);
}
}
/*
* Render the above component into the div#app
*/
React.render(<Application />, document.getElementById('app'));
<List title="Featured" tiles={this.data.featured} />
You can't use the self-closing / tag. You must use the </List> instead.
As Vivasaayi suggested, moving the logic to another function may be a good idea. However, if you want to have it all in one place, it could look something like this:
render() {
return (
<div className="lists-wrapper {this.data.user}">
{[
<List key="featured" title="Featured" tiles={this.data.featured} />,
this.data.user ?
<List key="Faves" title="Faves" tiles={this.data.faves} /> :
<List key="MyContent" title="My Content" tiles={this.data.owned} />
]}
</div>
);
}
Related
I am currently building a form builder with vue3 composition API. The user can add in different types of inputs like text, radio buttons etc into the form before saving the form. The saved form will then render with the appropriate HTML inputs. The user can edit the name of the question, eg Company Name <HTML textInput.
Currently, when the user adds an input type eg,text, the type is saved into an ordered array. I run a v-for through the ordered array and creating a custom component formComponent, passing in the type.
My formComponent renders out a basic text input for the user to edit the name of the question, and a place holder string for where the text input will be displayed. My issue is in trying to save the question text from the parent.
<div v-if="type=='text'">
<input type="text" placeholder="Key in title"/>
<span>Input field here</span>
</div>
I have an exportForm button in the parent file that when pressed should ideally return an ordered array of toString representations of all child components. I have tried playing with $emit but I have issue triggering the $emit on all child components from the parent; if I understand, $emit was designed for a parent component to listen to child events.
I have also tried using $refs in the forLoop. However, when I log the $refs they give me the div elements.
<div v-for="item in formItems" ref="formComponents">
<FormComponent :type="item" />
</div>
The ideal solution would be to define a method toString() inside each of the child components and have a forLoop running through the array of components to call toString() and append it to a string but I am unable to do that.
Any suggestions will be greatly appreciated!
At first:
You don't really need to access the child components, to get their values. You can bind them dynamically on your data. I would prefer this way, since it is more Vue conform way to work with reactive data.
But I have also implemented the other way you wanted to achieve, with accessing the child component's methods getValue().
I would not suggest to use toString() since it can be confused with internal JS toString() function.
In short:
the wrapping <div> is not necessary
the refs should be applied to the <FormComponents> (see Refs inside v-for)
this.$refs.formComponents returns the Array of your components
FormComponent is used here as <form-components> (see DOM Template Parsing Caveats)
The values are two-way bound with Component v-model
Here is the working playground with the both ways of achieving your goal.
Pay attention how the values are automatically changing in the FormItems data array.
const { createApp } = Vue;
const FormComponent = {
props: ['type', 'modelValue'],
emits: ['update:modelValue'],
template: '#form-component',
data() {
return { value: this.modelValue }
},
methods: {
getValue() {
return this.value;
}
}
}
const App = {
components: { FormComponent },
data() {
return {
formItems: [
{ type: 'text', value: null },
{ type: 'checkbox', value: false }
]
}
},
methods: {
getAllValues() {
let components = this.$refs.formComponents;
let values = [];
for(var i = 0; i < components.length; i++) {
values.push(components[i].getValue())
}
console.log(`values: ${values}`);
}
}
}
const app = createApp(App)
app.mount('#app')
#app { line-height: 2; }
[v-cloak] { display: none; }
label { font-weight: bold; }
th, td { padding: 0px 8px 0px 8px; }
<div id="app">
<label>FormItems:</label><br/>
<table border=1>
<thead><tr><th>#</th><th>Item Type:</th><th>Item Value</th></tr></thead>
<tbody><tr v-for="(item, index) in formItems" :key="index">
<td>{{index}}</td><td>{{item.type}}</td><td>{{item.value}}</td>
</tr></tbody>
</table>
<hr/>
<label>FormComponents:</label>
<form-component
v-for="(item, index) in formItems"
:type="item.type" v-model="item.value" :key="index" ref="formComponents">
</form-component>
<button type="button" #click="getAllValues">Get all values</button>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<script type="text/x-template" id="form-component">
<div>
<label>type:</label> {{type}},
<label>value:</label> <input :type='type' v-model="value" #input="$emit('update:modelValue', this.type=='checkbox' ? $event.target.checked : $event.target.value)" />
</div>
</script>
I am working to develop a custom block that allows the user to add tabs, and then populate each tab with as much content as desired using other gutenberg blocks. Its my understanding that there can only be a single instance of InnerBlocks in the edit / save function - even though that seems at first like how you would implement the functionality I am looking for (a single instance of InnerBlocks.Content for each tab.
So far, I have been unable to find a good resource on the high level methodology for implementing tabs or columns as a custom gutenberg block. I have reviewed some of the wordpress block library column code, however I find it to be pretty complex and abstract for beginner development of custom blocks.
I am hoping to receive guidance from a high level on how one would achieve the functionality I am looking for. Below I have included my very simple tabs block code for reference.
const { __ } = window.wp.i18n;
const { registerBlockType } = window.wp.blocks;
const { InnerBlocks, RichText } = window.wp.blockEditor;
registerBlockType('myplugin/tabs', {
title: __('Tabs', 'myplugin'),
category: 'widgets',
attributes: {
tabs: {
type: 'array',
default: []
}
},
edit: function ({
className,
attributes,
setAttributes,
}) {
let { tabs } = attributes;
return (
<div className={className}>
<button onClick={() => setAttributes({ tabs: [...tabs, { title: 'New Tab' }] })}>Add a tab</button>
{tabs.map((tab, i) => {
return (
<div key={i}>
<div>
<RichText
tagName="div"
value={tab.title}
onChange={(value) => {
tabs[i].title = value;
setAttributes({ tabs })
}}
/>
</div>
<div>
<InnerBlocks
allowedBlocks={ ALLOWED_BLOCKS }
/>
</div>
</div>
)
})}
</div>
)
},
save: function ({
attributes,
}) {
const { tabs } = attributes;
return (
<div>
{tabs.map((tab, i) => {
return (
<div key={i}>
<div>
{tab.title}
</div>
<div>
<InnerBlocks.Content />
</div>
</div>
)
})}
</div>
)
}
});
The pattern for creating blocks with multiple nested children is as follows. First define 2 block types, a parent and a child. Gutenberg core Block Columns is an example of this, there is "Columns Block" as the parent and then there is "Column Block" as the child block.
The use of the parent and child block enable us as developers to work around the restriction of only 1 InnerBlock region per block. It enables the structure using tabs as an example where we have:
/tabs-parent-block
/tab-child-block-1
/tab-child-block-2
/tab-child-block-3
These child blocks could still be within 1 InnerBlocks section which enables the user to add more child blocks. You can restrict the block types for child block to your custom tab child block using the <InnerBlocks allowedBlockType={ ALLOWED_BLOCK_TYPES } />.
What about a situation where putting all the child blocks in one area isn't practical? That is something I'm still experimenting with myself. It doesn't seem to matter for things like tabs because the parent/child approach should work. But what if it doesn't and you really need to have a parent block that has entirely different regions like for a page layout?
The reference shared in an earlier answer is relevant, this script registers 2 blocks which work together in the parent/child block pattern: https://github.com/Ultimate-Blocks/Ultimate-Blocks/blob/master/src/blocks/tabbed-content/components/tab.js
Notice that the first block registered is a singular tab (child block) ub/tab, the second block registered is the parent block ub/tab-block. In the parent block the save() function saves only the child tabs using <InnerBlocks.Content />.
The way to do this is to define another block which then to include in the parent block.
You can check out this plugin: https://github.com/Ultimate-Blocks/Ultimate-Blocks/tree/master/src/blocks/tabbed-content
I want to display a specific number of components around a component. How can I do that? :)
With this implementation, I draw 8 Patrat component
{Array.from(Array(8)).map((item, index) =>
(<Patrat key={index}/>)
)}
I want to display these 8 Patrat around a circle which is also a component.
After Understanding the issue and what you want here is the final solution
You can also create function which can dynamically create position and pass it to the Child components
Here is complete code resource link (Click Here)
Also your can experiment with some comment line in code link given above
You could create a recursive loop where you create a new Patrat component with the recursive call as children to it.
Example
function Patrat({ children }) {
return (
<div
style={{
paddingLeft: 10,
backgroundColor: "#" + Math.floor(Math.random() * 16777215).toString(16)
}}
>
{children}
</div>
);
}
function App() {
const content = (function patratLoop(num) {
if (num === 0) {
return <div> Foo </div>;
}
return <Patrat>{patratLoop(--num)}</Patrat>;
})(8);
return content;
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You could make the circle responsible for positioning the elements, then have Patrats as children of circle (Circle > div.circle_element_position > Patrat) or if Patrats will change depending on the parent component, you could use same logic but use renderProps for Patrats (Circle > div.circle_element_position > props.renderPatrat(index))
It would look more or less like this:
function Partat() {
return <div>1</div>
}
function calculatePositions(numberOfPatrats) {
//your implementation
}
function Circle ({numberOfPatrats}) {
let positions = calculatePositions(numberOfPatrats);
return <div className="circle">
{positions.map(
(position, index) => <div style={position} key={index}><Partat /></div>
)}
</div>
}
To place Parats on the positions you want you just need to implement calculatePositions, which will be similar to what you had in your jsfiddle example.
I like how the columns block works because it supports nesting. What I'm trying to do is similar to making the column block support 1 column (which you can force it to do, but it doesn't size correctly. This way, the entire block of multiple blocks and block types can be saved as a single reusable block instance.
Is this an instance where I need to start working on my own custom block type? Or will this be supported at some point?
What you need is InnerBlock. You can basically, group couple of blocks into a single custom block.
import { registerBlockType } from '#wordpress/blocks';
import { InnerBlocks } from '#wordpress/editor';
registerBlockType( 'my-plugin/my-block', {
// ...
edit( { className } ) {
return (
<div className={ className }>
<InnerBlocks />
</div>
);
},
save() {
return (
<div>
<InnerBlocks.Content />
</div>
);
}
} );
Btw, this is the main focus of Gutenberg Phase 2 development. And, Gutenberg will slowly take over into template building process.
I've recently started trying to use react-virtualized, but I've ran into an issue from the get-go. I've been trying to build a very simple grid, at first I loaded my data in and it wasn't working properly, but I've changed it to a simple 4x4 Grid and it's still giving me issues. Right now all 16 cells are being loading in a single column, and I've tried logging the rowIndex and the columnIndex, and those are giving me the correct output.
I'm not sure if I'm doing something wrong when I call the Grid, or if I'm doing something wrong with the cellRenderer, I would really appreciate some help with this. I have parts of my code down below.
_cellRenderer({columnIndex, key, rowIndex, styles}){
return(
<div>
{columnIndex}
</div>);
}
render(){
return(
<div>
<Autosizer>
{({width}) => (
<Grid
cellRenderer={this._cellRenderer}
columnCount={4}
columnWidth={30}
rowCount={4}
rowHeight={30}
width={400}
height={400}
/>
)}
</Autosizer>
</div>
);
}
You aren't using the parameters passed to your renderer. For example, style and key are both passed for a reason and you must use them. (The documentation should make this pretty clear.)
Put another way:
// Wrong:
function cellRenderer ({ columnIndex, key, rowIndex, style }) {
return (
<div>
{columnIndex}
</div>
)
}
// Right:
function cellRenderer ({ columnIndex, key, rowIndex, style }) {
return (
<div
key={key}
style={style}
>
{columnIndex}
</div>
)
}
Also, in case you didn't notice, the parameter is style and not styles like your code snippet shows.