How would I add a newline in a UL from JavaScript - javascript

So I am trying to add elements taken from an input box, put that into an array, and then display that in an unordered list. My problem is adding a <br> after each entry from Javascript after the value has been passed to the list.
Here is the code used to push to the list currently.
function addToList(){
var incompleteList = document.getElementById("incompleteTaskList");
incompleteList.append(currentTask);
}
Not sure how I would add a break after each new entry

You should be creating new <LI> elements and setting their text content to the task name.
const btnAdd = document.querySelector('.btn-add')
const addTask = e => {
let txtTask = document.querySelector('.txt-task'),
task = txtTask.value.trim()
if (task) {
let li = document.createElement('li')
li.textContent = task
document.querySelector('.tasks').append(li)
txtTask.value = ''
}
}
btnAdd.addEventListener('click', addTask)
h1 { font-size: 1.25rem; }
h2, label { font-size: 1.00rem; }
<h1>Task Manager</h1>
<div>
<label>New Task</label>
<input class="txt-task" type="text" />
<button class="btn-add">Add</button>
</div>
<h2>Tasks</h2>
<ul class="tasks"></ul>
Rendering application
Here is an example using a list data structure and rendering engine. It features auto-sorting and duplicate entry detection.
const main = () => {
new TaskManager('.task-app', {
formConfig: {
fieldLabel: 'New Task',
buttonText: 'Add'
},
listConfig: {
autoSort: true
}
})
}
class TaskForm {
constructor(options) {
let opts = Object.assign({}, TaskForm.defaultOptions, options)
this.fieldLabel = opts.fieldLabel
this.buttonText = opts.buttonText
}
addEvents(target) {
target.querySelector('.btn-add').addEventListener('click', e => {
let txtTask = document.querySelector('.txt-task'),
task = txtTask.value.trim()
if (task) {
const addEvent = new CustomEvent('task-add', {
detail: txtTask.value
})
target.dispatchEvent(addEvent);
txtTask.value = ''
}
})
}
render() {
return `
<div>
<label>${this.fieldLabel}</label>
<input class="txt-task" type="text" />
<button class="btn-add">${this.buttonText}</button>
</div>
`
}
}
TaskForm.defaultOptions = {
fieldLabel: 'New Task',
buttonText: 'Add'
}
class TaskList {
constructor(options) {
this.tasks = []
let opts = Object.assign({}, TaskList.defaultOptions, options)
this.autoSort = opts.autoSort
}
addTask(task) {
if (!this.tasks.includes(task)) {
this.tasks.push(task)
if (this.autoSort) this.tasks.sort()
} else {
console.log(`Task "${task}" already exists.`)
}
}
addEvents(target) {
target.addEventListener('task-add', e => {
this.addTask(e.detail)
const addEvent = new CustomEvent('task-added')
target.dispatchEvent(addEvent);
e.stopImmediatePropagation()
})
}
render() {
return `
<ul>
${this.tasks.map(task => `<li>${task}</li>`).join('')}
</ul>
`
}
}
TaskList.defaultOptions = {
autoSort: false
}
class Mountable {
constructor(target, options) {
this.target = typeof target === 'string' ?
document.querySelector(target) : target
this.initialize(options)
this.update()
}
update() {
this.target.innerHTML = this.render()
this.afterRender()
}
}
class TaskManager extends Mountable {
constructor(target, options) {
super(target, options)
}
initialize(options) {
this.form = new TaskForm(options.formConfig)
this.taskList = new TaskList(options.listConfig)
}
afterRender() {
this.target.addEventListener('task-added', e => {
this.update()
e.stopImmediatePropagation()
})
this.form.addEvents(this.target)
this.taskList.addEvents(this.target)
}
render() {
return `
<h1>Task Manager</h1>
${this.form.render()}
<h2>Tasks</h2>
${this.taskList.render()}
`
}
}
main()
h1 { font-size: 1.25rem; }
h2, label { font-size: 1.00rem; }
<div class="task-app"></div>

You should not be adding a <br> using javascript. That's not the correct implementation approach.
Instead, you need to style this using CSS. Try adding below style rule:
#incompleteTaskList li{display:block;}

Related

How do I get the correct item in a clickhandler

I have the following code:
HTML:
<div id="root">
<form class="todoForm">
<input class="input" placeholder="What's gotta be done'?" />
</form>
<div class="todos"></div>
<div class="footer">
<button class="All">All</button>
<button class="Active">Active</button>
<button class="Completed">Completed</button>
<button class="Clear">Clear</button>
</div>
</div>
CSS:
.todoForm {
margin-bottom: 20px;
}
.todos {
margin-bottom: 20px;
}
.todo {
margin-bottom: 10px;
}
.todoAndCheckBox {
display: flex;
font-weight: bold;
}
.checkBox {
border-radius: 50%;
outline: green;
}
.crossOut {
font-weight: normal;
text-decoration: line-through
}
JS:
const COMPLETED = 'completed';
const ACTIVE = 'active';
const ALL = 'all';
class displayTodos {
constructor(root) {
this.root = root;
this.input = this.root.querySelector('.input');
this.form = this.root.querySelector('.todoForm');
this.form.addEventListener('keydown', this.submitForm);
this.todos = this.root.querySelector('.todos');
this.store = {
todos: [
{
id: Math.random() * 10000,
text: 'Banana',
state: COMPLETED,
},
{
id: Math.random() * 10000,
text: 'Ice cream',
state: ACTIVE
}
],
}
this.AllButton = this.root.querySelector('.All');
this.ActiveButton = this.root.querySelector('.Active');
this.CompletedButton = this.root.querySelector('.Completed');
this.display();
}
submitForm = (e) => {
if(e.key === 'Enter') {
e.preventDefault();
const typed = this.input.value;
const newTodo = {
id: Math.random * 10000,
text: typed,
state: ACTIVE
}
const newTodos = [...this.store.todos, newTodo];
this.store.todos = newTodos;
this.display();
this.input.value = ''
}
}
display = () => {
while(this.todos.firstChild) {
this.todos.removeChild(this.todos.firstChild)
}
this.store.todos.forEach(todo => {
const { id, text, state } = todo;
const todoAndCheckBox = document.createElement('div');
const todoDiv = document.createElement('div');
todoAndCheckBox.classList.add('todoAndCheckBox');
todoDiv.innerText = todo.text;
const checkBox = document.createElement('input');
checkBox.setAttribute('type', 'checkbox');
checkBox.classList.add('checkBox');
this.todos.appendChild(todoAndCheckBox);
todoAndCheckBox.appendChild(checkBox);
todoAndCheckBox.appendChild(todoDiv);
todoAndCheckBox.classList.add('todo');
todoAndCheckBox.addEventListener('click', (e, todo) => this.clickHandler(e, todo));
this.displayCount(this.AllButton, ALL);
this.displayCount(this.ActiveButton, ACTIVE);
this.displayCount(this.CompletedButton, COMPLETED);
})
}
clickHandler = (e, todo) => {
e.currentTarget.classList.toggle('crossOut');
console.log(todo, 'todo')
todo.state = todo.state === COMPLETED ? ACTIVE : COMPLETED
}
displayCount = (button, type) => {
let count = 0;
if(type === ALL) {
count = this.store.todos.length;
button.innerText = `All: ${count}`;
}
if(type === ACTIVE) {
const filtered = this.store.todos.filter(todo => todo.state === ACTIVE);
count = filtered.length;
button.innerText = `Active: ${count}`;
}
if(type === COMPLETED) {
const filtered = this.store.todos.filter(todo => todo.state === ACTIVE);
count = filtered.length;
button.innerText = `Completed: ${count}`;
}
}
}
const root = document.querySelector('#root');
const instance = new displayTodos(root);
The desired outcome is when I click to cross out a todo item, the Active and Completed buttons will display the appropriate count number. However, in this function:
clickHandler = (e, todo) => {
e.currentTarget.classList.toggle('crossOut');
console.log(todo, 'todo')
todo.state = todo.state === COMPLETED ? ACTIVE : COMPLETED
}
I'm not able to get out the todo item. When I console.log it out, it says it is undefined. I'm confused because I did pass it down here using:
todoAndCheckBox.addEventListener('click', (e, todo) => this.clickHandler(e, todo));
Why am I not getting the todo and what can I do to get around it?
I noticed that if I rewrite that line to
todoAndCheckBox.addEventListener('click', this.clickHandler.bind(this, e, todo));
JS complains that e is not defined. How do I get e there?
You just need to remove the todo from the anonymous function's parameters in the listener.
By passing the todo as a parameter you are overwriting the todo that is already in scope, and the function is specting an argument that is not being passed to it.
Try this:
todoAndCheckBox.addEventListener('click', (e) => this.clickHandler(e, todo));
You can use an anonymous function in your listener. From there you could call another function or process your logic directly.
const otherFunction = (e) => {
console.log(todo); // global
console.log(e);
}
let todo = {
thing: 'stuff'
};
let todoAndCheckBox = document.getElementById('todoAndCheckBox');
todoAndCheckBox.addEventListener('click', (e) => otherFunction(e));
<button id='todoAndCheckBox'> click </button>

Trying to do a filter for a to-do list app in Javascript but struggling

I have a to-do list and it is working just fine but I'm trying to filter it but I'm struggling a little bit.
Basically, every task is an object every object has text, id, and checked properties.
I want for example to display only the tasks that are not checked when I press Active.
I hope you can help.
Here is an image for more clarification:
/* Selectors */
const switcher = document.querySelector(' input[Type="checkbox"]');
const body = document.body;
const formT = document.querySelector(`[data-new-todo-form]`)
const inputT = document.querySelector(`[data-new-todo-input]`)
const todoList = document.getElementById('todo-list');
const filterList = document.querySelector('.controls-list')
/* array that holds tasks */
let tasks = [];
/* EVENT LISTENERS */
switcher.addEventListener('change', (e) => {
if(e.target.checked) {
body.classList.replace('light', 'dark')
} else {
body.classList.replace('dark', 'light')
}
})
formT.addEventListener('submit', e => {
e.preventDefault()
let text = inputT.value.trim();
if(text !== '') {
addTask(text);
inputT.value = '';
inputT.focus();
}
})
todoList.addEventListener('click', e => {
if (e.target.classList.contains('js-tick')) {
const itemKey = e.target.parentElement.dataset.key;
toggleDone(itemKey);
}
if (e.target.classList.contains('delete')) {
const itemKey = e.target.parentElement.dataset.key;
deleteTodo(itemKey);
counter();
}
});
document.addEventListener('DOMContentLoaded', () => {
const ref = localStorage.getItem('tasksRef');
if (ref) {
tasks = JSON.parse(ref);
tasks.forEach(task => {
renderTodo(task);
counter();
});
}
});
/* FUNCTIONS */
/* create a todo object */
const addTask = (text) => {
const todoTask = {
text,
checked: false,
id: Date.now(),
}
tasks.push(todoTask);
renderTodo(todoTask);
};
const renderTodo = (todoTask)=> {
localStorage.setItem('tasksRef', JSON.stringify(tasks));
const item = document.querySelector(`[data-key='${todoTask.id}']`);
if (todoTask.deleted) {
// remove the item from the DOM
item.remove();
return
}
const isChecked = todoTask.checked ? 'done': '';
const node = document.createElement('li')
node.setAttribute('class', `todo-item ${isChecked}`);
node.setAttribute('data-key', todoTask.id);
node.innerHTML = `
<input class="js-tick" id="${todoTask.id}" type="checkbox" ${isChecked ? "checked" : ""}/>
<span>${todoTask.text}</span>
<img class="delete" width="15px" height='15px' src="/images/icon-cross.svg" alt="cross">`
;
todoList.append(node);
if (item) {
node.replaceWith(item)
} else {
todoList.append(node)
}
counter();
}
const toggleDone = (key) => {
const index = tasks.findIndex(task=> task.id === Number(key));
tasks[index].checked = !tasks[index].checked;
renderTodo(tasks[index]);
}
const deleteTodo = (key) => {
const index = tasks.findIndex(item => item.id === Number(key));
const todoTask = {
deleted: true,
...tasks[index]
};
tasks = tasks.filter(item => item.id !== Number(key));
renderTodo(todoTask);
}
const counter = () => {
const itemsCounter = tasks.filter(task=> !task.checked)
const count = document.getElementById('todosLeft');
const counterString = itemsCounter.length === 1 ? 'item' : 'items'
count.innerText = `${itemsCounter.length} ${counterString} left`
}
Here is the HTML:
<body class="light">
<section class="header">
</section>
<div class="container">
<div class="title-theme">
<h1>Todo</h1>
<input type="checkbox" id="switch-l" class="themeSwitch">
<label for="switch-l" id="switch" class="themeSwitch-label"></label>
</div>
<div class="todosInput">
<div id="mark"></div>
<form class="form" action="" data-new-todo-form>
<input id="todos" data-new-todo-input type="text" placeholder="Create a new todo..." >
</form>
</div>
<div class="myTodos">
<ul id="todo-list">
<--! THE TASKS GOES HERE !-->
</ul>
</div>
<div class="controls">
<p id="todosLeft">items left</p><!-- Add dynamic number -->
<div class="controls-list-div">
<ul class="controls-list" data-lists>
<li>All</li>
<li>Active</li>
<li>Completed</li>
</ul>
</div>
Clear Completed
</div>
</div>
<div class="instruc">
<p>Drag and drop to reorder list</p>
</div>
First you TodoList need to be immutable cause after you click all the TodoList should show all Todos.
const renderActiveTodos = () => {
const ref = localStorage.getItem('tasksRef');
if (ref) {
tasks = JSON.parse(ref);
tasks.filter(item => !item.check).forEach(task => {
renderTodo(task, true);
counter();
});
}
}
const renderAllTodos = () => {
const ref = localStorage.getItem('tasksRef');
if (ref) {
tasks = JSON.parse(ref);
tasks.forEach(task => {
renderTodo(task, true);
counter();
});
}
}
const renderCompletedTodos = () => {
const ref = localStorage.getItem('tasksRef');
if (ref) {
tasks = JSON.parse(ref);
tasks.filter(item => item.check).forEach(task => {
renderTodo(task, true);
counter();
});
}
}
But you should add preventMutableStorage in your renderTodo function to prevent the data in localstorage. Then you can return back the full todos List if you click all
const renderTodo = (todoTask, preventMutableStorage)=> {
if(!preventMutableStorage){
localStorage.setItem('tasksRef', JSON.stringify(tasks));
}
.....
}
Why I change renderTodo:
First assume we have todoList = [{id:1, check:false}, {id:2, check:true}, {id:3, check:true}]
When I click active then renderActiveTodos is called:
It get data from localstorage
Filter data with check=false => [{id:1, check:false}]
Then we loop through it then call renderTodo
In renderTodo it first set the localstorage again then now the Todo in Localstorage is [{id:1, check:false}]
Then next time we click all:
It can't show [{id:2, check:true}, {id:3, check:true}] cause we already update the todoList in localstorage
That is the reason I added preventMutableStorage param to prevent local storage TodoList update
There are something I just figure out in your code. But All will fixed this is work code.
https://codepen.io/nguyennghi3489/pen/gOwvNga?editors=1111

Click event on an inserted button to a contentEditable DIV doesn't work

I have a contentEditable DIV. I use the following Directive to manage it:
<div
class="msg-input-area"
[class.focused]="isMsgAreaFocused"
contenteditable
[contenteditableModel]="msgText"
(contenteditableModelChange)="onMsgTextChange($event)"
(contenteditableModelSubmit)="onMsgSend()"
(removeHashSign)="removeHashSign()"
placeholder="Compose your comment here"
tabindex="0"
(focus)="isMsgAreaFocused = true"
(blur)="isMsgAreaFocused = false"
#msgInputArea
></div>
#Directive({
selector: '[contenteditableModel]',
host: {
'(blur)': 'onEdit()',
'(keydown)': 'onKeyDown($event)',
'(keyup)': 'onEdit()'
}
})
export class ContentEditableDirective implements OnChanges {
#Input('contenteditableModel') model: string;
#Output('contenteditableModelChange') valueChanges = new EventEmitter<string>();
#Output('contenteditableModelSubmit') submit = new EventEmitter<void>();
#Output('removeHashSign') removeHashSign = new EventEmitter<void>();
constructor(private elementRef: ElementRef<HTMLElement>) {}
ngOnChanges(changes: SimpleChanges) {
if (changes.model) {
this.refreshView();
}
}
onEdit(): void {
const value = this.elementRef.nativeElement.innerText;
this.valueChanges.emit(value);
}
onKeyDown(event: KeyboardEvent): void {
if (!(event.ctrlKey || event.metaKey) && event.code === 'Enter') {
// submit
this.valueChanges.emit(this.elementRef.nativeElement.innerHTML);
this.submit.emit();
event.preventDefault();
return;
} else if (event.code === 'Backspace' && this.model[this.model.length - 1] === '#') {
// remove #
this.removeHashSign.emit();
return;
}
this.onEdit();
}
private refreshView(): void {
if (!this.model) {
this.elementRef.nativeElement.textContent = this.model;
} else if (this.model?.includes('<button')) {
this.elementRef.nativeElement.innerHTML = this.model;
}
}
}
If # sign is entered then a button element is added inside the DIV
... => {
let msgWithTag = this.msgText.slice(0, -1);
msgWithTag += this.generateLinkTag(tag);
this.msgText = msgWithTag;
}
...
private generateLinkTag(tag: IConversationTag): string {
return `<button style="color: #0d95db; font-weight: 600; cursor: pointer;">#${tag.name}</button> `;
}
So far so good. The button is shown inside the DOM.
How can I add (click) event to the button?
I've tried couple of ways:
return `<button (click)="() => myFunc()" style="color: #0d95db; font-weight: 600; cursor: pointer;">#${tag.name}</button> `;
and
ngAfterViewInit() {
const parser = new DOMParser();
const doc = parser.parseFromString(myString, 'text/html');
doc.body.childNodes.forEach((node: ChildNode) => {
if (node.nodeName.toLowerCase() === 'button') {
console.log(node)
node.addEventListener('click', () => {
console.log('click')
});
}
});
if (this.msgContent) {
this.msgContent.nativeElement.innerHTML = doc.body.innerHTML;
}
}
In both cases (click) callback functions are not invoked.
For the click handler on the button the syntax should be (click)="myFunc()" where myFunc is a function defined in your component.
https://angular.io/guide/event-binding

How to list all suggestions and filtered suggestions based on user input using reactjs?

i want to show all available usernames when user types # in input field and filtered usernames when user enters anything after # character.
I have implemented like below,
class UserMention extends React.purecomponent {
constructor(props) {
super(props);
this.state = {
text: '',
user_mention: false,
};
this.user='';
}
user_list = [
{name: 'John smith'},
{name: 'Jenna surname2'},
{name: 'Tuija rajala'},
];
get_user = s => s.includes('#') && s.substr(s.lastIndexOf('#') +
1).split(' ')[0];
handle_input_change = (event) => {
let user_mention;
this.user = this.get_user(event.target.value);
if (event.target.value.endsWith('#')) {
user_mention = true;
} else {
user_mention = false;
}
this.setState({
user_mention: user_mention,
[event.target.name]: event.target.value,
});
};
get_text_with_user_mention = (text, selected_user) => {
let user_name = selected_user;
let text_without_user_mention;
text_without_user_mention = text.slice(0,
text.lastIndexOf('#'));
return text_without_user_mention + user_name;
};
handle_select_value = (selected_user) => {
let text;
text = this.get_text_with_user_mention(this.state.text,
selected_user);
this.setState({
text: text,
user_mention: false,
});
this.user = false;
};
render = () => {
let suggested_values = [];
if (this.state.user_mention) {
suggested_values = this.user_list
.map((o) => { return {user_name: o.user_name};});
}
if (this.user) {
suggested_values = this.user_list
.filter(user => user.user_name.indexOf(this.user) !==
-1)
.map((o) => {return {user_name: o.user_name};});
}
return (
<input
required
name="text"
value={this.state.text}
onChange={this.handle_input_change}
type="text"/>
{this.state.user_mention &&
<SelectInput
on_change={this.handle_select_value}
values={suggested_values}/>}
{this.user &&
<SelectInput
on_change={this.handle_select_value}
values={suggested_values}/>}
);
};
}
As you see from above code, i am modifying suggested_values based on this.user and this.state.user_mention state. Can someone help me refactor or modify this a bit more nicer. thanks.
This is another approach using React hooks, instead of classes. If you've never worked with hooks, give it a try. You will enjoy it. It's much simpler in my opinion.
I also added a username property. It's much better if you work with a string that doesn't allow spaces when you're tagging someone. You can also display the full name with spaces along with the username, if you wish.
Ex:
John Smith (#johnsmith)
function App() {
const inputRef = React.useRef(null);
const [inputValue, setInputValue] = React.useState('');
const [userList,setUserList] = React.useState([
{name: 'John smith', username:'johnsmith'},
{name: 'Jenna surname2', username:'jennasurname2'},
{name: 'Tuija rajala', username:'tuijarajala'}
]
);
const [showSuggestions,setShowSuggestions] = React.useState(false);
const [suggestionList,setSuggestionList] = React.useState(
['johnsmith','jennasurname2','tuijarajala']
);
function onChange(event) {
const regexp = /#[a-zA-Z0-9]*$/;
if (regexp.test(event.target.value)) {
setShowSuggestions(true);
}
else {
setShowSuggestions(false);
}
setInputValue(event.target.value);
}
function focusInput() {
inputRef.current.focus();
}
return(
<React.Fragment>
<input ref={inputRef} type='text' value={inputValue} onChange={onChange}/>
{showSuggestions &&
<Suggestions
inputValue={inputValue}
suggestionList={suggestionList}
applyMention={onChange}
focusInput={focusInput}
/>
}
</React.Fragment>
);
}
function Suggestions(props) {
function selectSuggestion(username) {
const regexp = /#[a-zA-Z0-9]*$/;
const newValue = props.inputValue.replace(regexp,username + ' ');
props.applyMention({target: {value: newValue}}); // THIS MIMICS AN ONCHANGE EVENT
props.focusInput();
}
const suggestionItems = props.suggestionList.map((item) =>
<div className="item" onClick={()=>selectSuggestion('#' + item)}>#{item}</div>
);
return(
<div className="container">
{suggestionItems}
</div>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
.container {
border: 1px solid silver;
width: 150px;
}
.item {
cursor: pointer;
}
.item:hover {
color: blue;
}
input {
width: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
You can simplify your code by doing something like this.
See sandbox: https://codesandbox.io/s/react-example-kgm2h
import ReactDOM from "react-dom";
import React from "react";
class UserMention extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "",
user_list: [
{ name: "John smith" },
{ name: "Jenna surname2" },
{ name: "Tuija rajala" }
],
suggestions: []
};
}
handleOnChange = e => {
const { value } = e.target;
const { user_list } = this.state;
//show all user suggestions
if (value.includes("#") && value.indexOf("#") === value.length - 1) {
this.setState({
text: value,
suggestions: [...this.state.user_list]
});
//show matching user suggesstions
} else if (value.includes("#") && value.length > 1) {
const stringAfterAt = value.slice(value.indexOf("#") + 1).toLowerCase();
const newSuggestions = user_list.filter(user => {
return user.name.toLowerCase().includes(stringAfterAt);
});
this.setState({
text: value,
suggestions: newSuggestions
});
//display no users if they do not use the # symbol
} else {
this.setState({
text: value,
suggestions: []
});
}
};
createSuggestionsList = () => {
const { suggestions } = this.state;
return suggestions.map(user => {
return <div>{user.name}</div>;
});
};
render = () => {
return (
<div>
<input
required
name="text"
value={this.state.text}
onChange={this.handleOnChange}
type="text"
/>
{this.createSuggestionsList()}
{/* <SelectInput value={this.state.suggestions}/> */}
</div>
);
};
}
ReactDOM.render(<UserMention />, document.getElementById("root"));
I'm not entirely sure how you want to render the suggested users, but you can always just pass down this.state.suggestions as a prop to the SelectInput component.
Main takeaway is to use an additional array in our state for suggestions and update it as the user types into the input. We call {this.createSuggestionsList()} inside render to dynamically create the markup for each suggested user. Or as mentioned above, just pass down the suggestions as a prop.

Objects are not valid as a React child (found: object with keys)

I create a component like so:
let bList = bObj.map((obj, index) => {
let {
icon, htmlType, onClick } = obj;
let _b = <Button
htmlType = { htmlType }
icon = { icon }
onClick = { () => {this._onClick()} } />
if(someVar) {
return (
<AnotherComp style = { someVar }
key = { index } >
{ _b }
</AnotherComp>
);
} else {
return (
{ _b }
);
}
});
bList =
<div style = { wrap }>
<myComp style = { grp }>
{ buttonsList }
</myComp>
</div>
return bList;
That returns me
Uncaught Error: Objects are not valid as a React child (found: object with keys {_bu}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of MyComp.
However, when I write it like this:
let bList = bObj.map((obj, index) => {
let {
icon, htmlType, onClick } = obj;
if(someVar) {
return (
<AnotherComp style = { someVar }
key = { index } >
<Button
htmlType = { htmlType }
icon = { icon }
onClick = { () => {this._onClick()} } />
</AnotherComp>
);
} else {
return (
<Button
htmlType = { htmlType }
icon = { icon }
onClick = { () => {this._onClick()} } />
);
}
});
bList =
<div style = { wrap }>
<MyComp style = { grp }>
{ buttonsList }
</MyComp>
</div>
return bList;
It works. Where is the difference between saving <Button ../> in a variable and writing it in there directly?!
Difference is you are using extra {}, remove that it will work:
return _b;
Meaning of return ({ _b }) is:
return ({'_b' : _b});
That means you are returning an object with key _b, not the JSX.
Check this snippet:
let a = 5;
let b = { a };
console.log('b = ', b);

Categories