Returning a div component after onClick event in React and Typescript - javascript

I am working on a to-do list with React and TypeScript. I have a "done" button in the DOM for showing my completed tasks after I have checked them. I am trying to see if I can return anything at the moment before adding more information. Here is what I have so far, I have tried to add a useState hook to the function (see below):
const ShowDone = () => {
const [showTasks, setShowTasks] = useState(false)
const onClick = () => setShowTasks(true)
return (
<div>
<button className="doneButton" onClick={onClick}>Done</button>
{ showTasks ? <Tasks /> : null }
</div>
)
}
const Tasks = () => (
<div id="results" className="task-results">
Some Results
</div>
);
the above looked OK to me but when I click the done button in dev tools, nothing happens and I get the following error message, stating that it is an invalid hook call:
throw new Error('Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + '2. You might be breaking the Rules of Hooks\n' + '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.');
}
I erased everything and just added console.log('hello') into the ShowDone function and that was fine, nothing wrong with the button itself. Not sure why this is occurring, did I put this function on the wrong line number in my App.tsx file? What about the invalid hooks error? What about my code makes this invalid?
Edit: I have added more of my code below, this is what is below the ShowDone function above.
const initialTodosString = localStorage.getItem("toDoList");
const initialTodos = initialTodosString
? JSON.parse(initialTodosString)
: [myToDo1, myToDo2];
function App(): JSX.Element {
const [toDos, setToDos] = useState<ToDo[]>(initialTodos);
const [addingToDo, setAddingToDo] = useState(false);
useEffect(
function () {
localStorage.setItem("toDoList", JSON.stringify(toDos));
},
[toDos]
);
function newTask() {
setAddingToDo(true);
}
function handleFormSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const data = Object.fromEntries(
new FormData(event.target as HTMLFormElement)
);
setToDos([
...toDos,
{
title: data.Title as string,
priority: parseInt(data.Priority as string) as 2 | 1,
description: data.Description as string,
checked: false || true,
},
]);
}
if (addingToDo) {
return (
<div className="form-container">
<form onSubmit={handleFormSubmit}>
<h1>To Do Form </h1>
<p>
<label>Title</label>
</p>
<p>
<input name="Title" className="input-field" id="title" />
</p>
<p>
<label>Due Date</label>
</p>
<p>
<input name="Date" type="date" className="input-field" />
</p>
<p>
<label>Priority</label>
</p>
<p>
<select name="Priority" className="input-field">
<option className="important" value="1">
Important
</option>
<option selected value="2">
Normal
</option>
{/* <option>Urgent</option> */}
</select>
</p>
<p>
<label>Description</label>
</p>
<p>
<input name="Description" className="input-field" />
</p>
<p>
<input type="submit" value="Add" className="submit" />
</p>
</form>
</div>
);
}
return (
<div className="App">
<div className="greeting-container">
<div className="greeting">
<Greeting />
</div>
<button className="task-button" onClick={newTask}>
New Task
</button>
<div className="date-container">
Today is {new Date().toLocaleString("en-US", { weekday: "long" })}
<br />
<div className="current-date">
{new Date().toLocaleString("en-US", {
month: "long",
day: "2-digit",
})}
, {new Date().getFullYear()}
</div>
</div>
</div>
<div className="task-container">
<div className="task-counter">
{toDos.length} {toDos.length === 1 ? "Task" : "Tasks"}
</div>
<div className="status-container">
<button className="activeButton">Active</button>
<button className="doneButton" onClick={ShowDone}>Done</button>
</div>
</div>
<hr />

It makes no sense to have an event handler, like your ShowDone, return JSX - or indeed anything at all. Like all event handlers, this will just (possibly) perform some side effects - the browser will completely ignore any return value you specify. (In your actual code your event handler pretends to be a component and calls the useState hook, thus breaking the Rules of Hooks as your error message says - and returns some JSX output for the button itself which was already rendered by App, which doesn't make sense at all.)
So here's how to actually do what I think you were trying to do. (Not the complete App component but just explaining the bits you need to add to it.) You need the showTasks state, and the conditional rendering of the Tasks component based on it, inside App itself. I've also altered the name of your event handler to showDone, starting with lower case, as it's convention in React that only functions representing components should start with an uppercase later:
function App(): JSX.Element {
const [showTasks, setShowTasks] = useState(false)
// somewhere in your component include this:
const showDone = () => setShowTasks(true);
// the rest of the component exactly as you had it
return (
// JSX output as you had it before, including this button:
<button className="doneButton" onClick={showDone}>Done</button>
// and including this at the appropriate place in the markup:
{ showTasks ? <Tasks /> : null }
);
}

Related

React: Form not re-rendering when updated

Disclaimer: I am new to React. I am trying to set up a form where selecting a different radio button at the top changes the list of checkboxes below to a different list, the text for which I've stored in four arrays within an object (formVersions). See code below for my setup. The radio buttons have an onClick function called radioButtonClick that puts together the component from the text in the corresponding array and sets the variable formList to this, and since I've put formList in the App() function that gets exported to the renderer, I would expect this to change the list of checkboxes on radio button click. However, it does not. The list is populated and rendered properly when radioButtonClick runs the first time on page load, but clicking on the radio buttons does not update the actual component on the page for whatever reason (though the function runs).
Not sure if my issue is due to something fundamental about how React works that I don't yet know, or some minor tweak that needs to be made. I am hoping for a solution that does not require that much reconfiguration, even if my method is not the typical way of doing this, but obviously that may end up not being possible. Thank you!
import './App.css';
var selectedVersion = 'metal-tearoff';
var formVersions = {
'metal-tearoff': [
'a',
'b',
'c'
],
'metal-layover': [
'd',
'e',
'f'
],
'shingle-tearoff': [
'a',
'b',
'c'
],
'shingle-layover': [
'd',
'e',
'f'
]
}
var formList = [];
function radioButtonClick(id) {
selectedVersion = id;
formList = [];
for (var i = 0; i < formVersions[id].length; i += 1) {
formList.push(
<div key={i.toString()}>
<input type="checkbox" name={formVersions[id][i]} id={formVersions[id][i]} className = "checkbox" defaultChecked/>
<label htmlFor={formVersions[id][i]}>{formVersions[id][i]}</label>
</div>
)
}
}
radioButtonClick('metal-tearoff')
function App() {
return (
<div id = "main-div">
<form onSubmit={() => {}} method="post" action="https://postman-echo.com/post">
<input type="radio" name="roof-type" id="metal-tearoff" onClick= {() => {radioButtonClick('metal-tearoff')}} defaultChecked/>
<label htmlFor="always">Metal Tearoff</label>
<input type="radio" name="roof-type" id="metal-layover" onClick= {() => {radioButtonClick('metal-layover')}}/>
<label htmlFor="never">Metal Layover</label>
<input type="radio" name="roof-type" id="shingle-tearoff" onClick= {() => {radioButtonClick('shingle-tearoff')}}/>
<label htmlFor="change">Shingle Tearoff</label>
<input type="radio" name="roof-type" id="shingle-layover" onClick= {() => {radioButtonClick('shingle-layover')}}/>
<label htmlFor="change">Shingle Layover</label>
<div>
{formList}
</div>
<div>
<input type="submit" name="submit" value="Submit" />
</div>
</form>
</div>
);
}
export default App;
Alex, your issue is in the way you are managing the state of your component. Think of your App function as a render function that React invokes many times (more times than you need to worry about in most cases). The variables and functions you have created outside of your App function do not ever change, while the App function gets called. In order to work with this, your App component has its own idea of state which you should manage. Hooks such as useState help you manage this very succinctly. I think of it like this: React decides to call your render (or functional component) function at its discretion, it will run through all of the code in your function. When it encounters a hook such as useState, it will reuse values that it saw before (unless the optional dependencies of the hook have changed in this invocation). Not sure if that helps you or not. Here is a working fork of your original:
https://codesandbox.io/s/react-form-question-1-forked-7qodq8?file=/src/App.js
Yes, you are newbie React. You should watch more toturial and read document.
I changed many line code for you
All variales you need wrap it into Component( here is App Component )
You research useState , useEffect and useMemo
Here I setState selectedVersion , and I watched when selectedVersion changed, useMemo will run everytime when selectedVersion changed with params second of useMemo [selectedVersion],and the App will re-render
Hope help you ^^, If you code a lot, you will understand. Dont worry
import { useMemo, useState } from "react";
var formVersions = {
"metal-tearoff": ["a", "b", "c"],
"metal-layover": ["d", "e", "f"],
"shingle-tearoff": ["a", "b", "c"],
"shingle-layover": ["d", "e", "f"]
};
function App() {
const [selectedVersion,setSelectedVersion]= useState("metal-tearoff")
const renderFormList=useMemo(()=>{
let formList = [];
for (var i = 0; i < formVersions[selectedVersion].length; i += 1) {
formList.push(
<div key={i.toString()}>
<input
type="checkbox"
name={formVersions[selectedVersion][i]}
id={formVersions[selectedVersion][i]}
className="checkbox"
defaultChecked
/>
<label htmlFor={formVersions[selectedVersion][i]}>{formVersions[selectedVersion][i]}</label>
</div>
);
}
return formList;
},[selectedVersion])
function radioButtonClick(id) {
setSelectedVersion(id)
}
return (
<div id="main-div">
<form
onSubmit={() => {}}
method="post"
action="https://postman-echo.com/post"
>
<input
type="radio"
name="roof-type"
id="metal-tearoff"
onClick={() => {
radioButtonClick("metal-tearoff");
}}
defaultChecked
/>
<label htmlFor="always">Metal Tearoff</label>
<input
type="radio"
name="roof-type"
id="metal-layover"
onClick={() => {
radioButtonClick("metal-layover");
}}
/>
<label htmlFor="never">Metal Layover</label>
<input
type="radio"
name="roof-type"
id="shingle-tearoff"
onClick={() => {
radioButtonClick("shingle-tearoff");
}}
/>
<label htmlFor="change">Shingle Tearoff</label>
<input
type="radio"
name="roof-type"
id="shingle-layover"
onClick={() => {
radioButtonClick("shingle-layover");
}}
/>
<label htmlFor="change">Shingle Layover</label>
<div>{renderFormList}</div>
<div>
<input type="submit" name="submit" value="Submit" />
</div>
</form>
</div>
);
}
export default App;

Remove dynamic rendered element from dom in ReactJS

Currently I've got a react component that looks like this:
const GeraCard = (cards, cart = false) => {
return cards.map((v, i) => {
return (
<div key={i} className={styles.card}>
<div onClick={() => urlRender(v.url)} className={styles.cardContent}>
<div>
<span className={styles.cardTitulo}>{v.Nome}</span>
</div>
<div>
<span className={styles.cardData}>{v.Data}</span>
<span className={styles.cardAtivos}>{v.Ativos} ativo(s)</span>
</div>
{cart ? <div>R$ {FormatCapital(v.Capital)}</div> : null}
</div>
<span className={styles.trash}>
<FontAwesomeIcon
icon={faTrash}
color={"#3c3c3c77"}
onClick={(e) => {
e.persist()
TrashHandler(v.Nome, e)
}}
/>
</span>
</div>
);
});
};
Based on the cards array, it renders something like this:
Rendered Component
Whenever I click the trash button, I make a request to my backend, edit the list on my database and rerender the component based on the now updated "cards". The problem is that this takes sometime to happen, so i wanted a way to remove it from the dom instantly while my backend does it's job.
somehting like
{show ? renderCompoennt : null}
I've tried using vanilla javascript to grab the parent from the trash can, which would be the card i want to remove, but the results are unpredictable and it's quite slow as well.
My latest try was this:
const GeraCard = (cards, cart = false) => {
return cards.map((v, i) => {
const [show, setShow] = useState(true);
return (
<div key={i}>
{show ?
<div className={styles.card}>
<div onClick={() => urlRender(v.url)} className={styles.cardContent}>
<div>
<span className={styles.cardTitulo}>{v.Nome}</span>
</div>
<div>
<span className={styles.cardData}>{v.Data}</span>
<span className={styles.cardAtivos}>{v.Ativos} ativo(s)</span>
</div>
{cart ? <div>R$ {FormatCapital(v.Capital)}</div> : null}
</div>
<span className={styles.trash}>
<FontAwesomeIcon
icon={faTrash}
color={"#3c3c3c77"}
onClick={(e) => {
setShow(false);
e.persist()
TrashHandler(v.Nome, e)
}}
/>
</span>
</div> :
null
}
</div>
);
});
};
but react won't let me do this. Even tho its fast, everytime one item gets deleted, react complains that "less hooks were rendered" and crashes the app.
You are attempting to do some Optimistic UI, in which you assume that your action will succeed, and reflect the expected/assumed state instantly, before the request to the backend completes. This would be in lieu of showing some progress/busy indicator, like a spinner, until the action completes with the server.
The first problem and immediate problem in your code-- it violates the rules of hooks, which state that hooks may only be used at the top-level (never inside loops, conditionals, etc).
The second problem is that you are leveraging vanilla JS to manipulate the DOM directly; this generally an antipattern in MV* frameworks, and very much so here. Instead, I would suggest doing managing it in your data model; something like this:
Rewrite your .map handler to return null if the card has a deleted property.
When the user clicks the trash button, do two things:
Make the request to the backend to delete it
Use a setState to add a deleted: true property to the clicked card
Now you will get a rerender that will omit the deleted card, and also make the request to the backend, all inside the React data model. Make sure that you handle complexity for:
How to handle the response
How to handle an error if the deletion fails at the backend
How to manage if a user quickly clicks many cards for deletion before any of the requests can complete.
The problem is that in the first render you have {cards.length} calls to hook "useState" within GeraCard, but after deletion of one card, you will have {cards.length-1} calls to hook "useState". As the React docs state:
Don’t call Hooks inside loops, conditions, or nested functions.
Instead, always use Hooks at the top level of your React function. By
following this rule, you ensure that Hooks are called in the same
order each time a component renders. That’s what allows React to
correctly preserve the state of Hooks between multiple useState and
useEffect calls.
You should extract the content of map callback into separate a component.
const GeraCards = (cards, cart = false) => {
return cards.map((v, i) =>
<GeraCard card={v} index={i} cart={cart} />
);
};
const GeraCard = ({ card, index, cart }) => {
const [show, setShow] = useState(true);
const v = card;
return (
<div key={index}>
{show ?
<div className={styles.card}>
<div onClick={() => urlRender(v.url)} className={styles.cardContent}>
<div>
<span className={styles.cardTitulo}>{v.Nome}</span>
</div>
<div>
<span className={styles.cardData}>{v.Data}</span>
<span className={styles.cardAtivos}>{v.Ativos} ativo(s)</span>
</div>
{cart ? <div>R$ {FormatCapital(v.Capital)}</div> : null}
</div>
<span className={styles.trash}>
<FontAwesomeIcon
icon={faTrash}
color={"#3c3c3c77"}
onClick={(e) => {
setShow(false);
e.persist()
TrashHandler(v.Nome, e)
}}
/>
</span>
</div> :
null
}
</div>
);
}

onChange event handler function attached with input field to update the state of the app executes once on first load and twice afterwords

I'm building a simple form, inputFields is basically the state; which is an array of object with inputs and handleInputChange calls the setInputsFields which updates the state...
you see i have a adult:true property, im using checkbox in html form to update this field...
The issue: on initial load of app i observe that handleInputChange is executed once (handleInputChange called logged once), then after each change in any of input this functions executes twice (handleInputChange called logged 3, 5, 7 and so on) ... apart from the fact that it negates the logic of how i update the adult:true or false state, why the handleInputChange function is being executed twice...
export default function App() {
const [inputFields, setInputFields] = useState(()=>{
return [{ userName:'', age: 0, adult: true}]
})
const handleInputChange = ({name, value, type}, index) => {
setInputFields( prevInputField => {
if(type === "checkbox"){
console.log('handleInputChange called')
prevInputField[index][name]=!prevInputField[index][name]
} else{
console.log('handleInputChange called')
prevInputField[index][name]= value
}
return [...prevInputField]
})
}
return(
inputFields.map((inputField,index,inputFields)=> {
return (
<>
<input type="text" id="userName" name="userName" value={inputField.userName} onChange={(event)=> handleInputChange(event.target,index)} />
<input type="number" id="age" name="age" value={inputField.age} onChange={(event)=> handleInputChange(event.target,index)} />
<input type="checkbox" id="adult" value={inputField.adult} name="adult" defaultChecked={ inputField.adult ? true : false } onClick={(event)=> handleInputChange(event.target,index)} />
</>
)
})
)//ends return
}// ends App
Adding it here instead of comments, as it required a code example. #mhk Here I observed it.
const {useEffect, useState} = React
function App() {
const [inputFields, setInputFields] = useState(()=>{
return [{ userName:'', age: 0, adult: true}]
})
const handleInputChange = ({name, value, type}, index) => {
setInputFields( prevInputField => {
if(type === "checkbox"){
console.log('handleInputChange called')
prevInputField[index][name]=!prevInputField[index][name]
} else{
console.log('handleInputChange called')
prevInputField[index][name]= value
}
return [...prevInputField]
})
}
return(
inputFields.map((inputField,index,inputFields)=> {
return (
<div>
<input type="text" id="userName" name="userName" value={inputField.userName} onChange={(event)=> handleInputChange(event.target,index)} />
<input type="number" id="age" name="age" value={inputField.age} onChange={(event)=> handleInputChange(event.target,index)} />
<input type="checkbox" id="adult" value={inputField.adult} name="adult" defaultChecked={ inputField.adult ? true : false } onClick={(event)=> handleInputChange(event.target,index)} />
</div>
)
})
)
}
ReactDOM.render(<App />, document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
okay after hours of re-search , seems like its a universal issue, perhaps the better phrase for the issue would be "onChange even firing twice in react.js"
any ways, as i suspects there was something wrong (read it set) within the configs, as the following page rightly directed me this
https://www.mmbyte.com/article/91391.html
it was the react.strictMode which was enabled by default, which was causing double render of the components...
as suggested in here as well:
My React Component is rendering twice because of Strict Mode
"StrictMode renders components twice (on dev but not production) in order to detect any problems with your code and warn you about them (which can be quite useful).
If you have StrictMode enabled in your app but don't remember enabling it, it might be because you used create-react-app or similar to create your app initially, which automatically enables StrictMode by default."
I disabled strictMode in index.js and now double firing is stopped...

eslint error about .jsx flie tag in vue-project when I npm run dev [duplicate]

I am trying to set up my React.js app so that it only renders if a variable I have set is true.
The way my render function is set up looks like:
render: function() {
var text = this.state.submitted ? 'Thank you! Expect a follow up at '+email+' soon!' : 'Enter your email to request early access:';
var style = this.state.submitted ? {"backgroundColor": "rgba(26, 188, 156, 0.4)"} : {};
return (
<div>
if(this.state.submitted==false)
{
<input type="email" className="input_field" onChange={this._updateInputValue} ref="email" value={this.state.email} />
<ReactCSSTransitionGroup transitionName="example" transitionAppear={true}>
<div className="button-row">
<a href="#" className="button" onClick={this.saveAndContinue}>Request Invite</a>
</div>
</ReactCSSTransitionGroup>
}
</div>
)
},
Basically, the important portion here is the if(this.state.submitted==false) portion (I want these div elements to show up when the submitted variable is set to false).
But when running this, I get the error in the question:
Uncaught Error: Parse Error: Line 38: Adjacent JSX elements must be wrapped in an enclosing tag
What is the issue here? And what can I use to make this work?
You should put your component between an enclosing tag, Which means:
// WRONG!
return (
<Comp1 />
<Comp2 />
)
Instead:
// Correct
return (
<div>
<Comp1 />
<Comp2 />
</div>
)
Edit: Per Joe Clay's comment about the Fragments API
// More Correct
return (
<React.Fragment>
<Comp1 />
<Comp2 />
</React.Fragment>
)
// Short syntax
return (
<>
<Comp1 />
<Comp2 />
</>
)
It is late to answer this question but I thought It will add to the explanation.
It is happening because any where in your code you are returning two elements simultaneously.
e.g
return(
<div id="div1"></div>
<div id="div1"></div>
)
It should be wrapped in a parent element. e.g
return(
<div id="parent">
<div id="div1"></div>
<div id="div1"></div>
</div>
)
More Detailed Explanation
Your below jsx code get transformed
class App extends React.Component {
render(){
return (
<div>
<h1>Welcome to React</h1>
</div>
);
}
}
into this
_createClass(App, [{
key: 'render',
value: function render() {
return React.createElement(
'div',
null,
React.createElement(
'h1',
null,
'Welcome to React'
)
);
}
}]);
But if you do this
class App extends React.Component {
render(){
return (
<h1>Welcome to React</h1>
<div>Hi</div>
);
}
}
this gets converted into this (Just for illustration purpose, actually you will get error : Adjacent JSX elements must be wrapped in an enclosing tag)
_createClass(App, [{
key: 'render',
value: function render() {
return React.createElement(
'div',
null,
'Hi'
);
return React.createElement(
'h1',
null,
'Welcome to React'
)
}
}]);
In the above code you can see that you are trying to return twice from a method call, which is obviously wrong.
Edit- Latest changes in React 16 and own-wards:
If you do not want to add extra div to wrap around and want to return more than one child components you can go with React.Fragments.
React.Fragments (<React.Fragments>)are little bit faster and has less memory usage (no need to create an extra DOM node, less cluttered DOM tree).
e.g (In React 16.2.0)
render() {
return (
<>
React fragments.
<h2>A heading</h2>
More React fragments.
<h2>Another heading</h2>
Even more React fragments.
</>
);
}
or
render() {
return (
<React.Fragments>
React fragments.
<h2>A heading</h2>
More React fragments.
<h2>Another heading</h2>
Even more React fragments.
</React.Fragments>
);
}
or
render() {
return [
"Some text.",
<h2 key="heading-1">A heading</h2>,
"More text.",
<h2 key="heading-2">Another heading</h2>,
"Even more text."
];
}
React element has to return only one element. You'll have to wrap both of your tags with another element tag.
I can also see that your render function is not returning anything. This is how your component should look like:
var app = React.createClass({
render () {
/*React element can only return one element*/
return (
<div></div>
)
}
})
Also note that you can't use if statements inside of a returned element:
render: function() {
var text = this.state.submitted ? 'Thank you! Expect a follow up at '+email+' soon!' : 'Enter your email to request early access:';
var style = this.state.submitted ? {"backgroundColor": "rgba(26, 188, 156, 0.4)"} : {};
if(this.state.submitted==false) {
return <YourJSX />
} else {
return <YourOtherJSX />
}
},
If you don't want to wrap it in another div as other answers have suggested, you can also wrap it in an array and it will work.
// Wrong!
return (
<Comp1 />
<Comp2 />
)
It can be written as:
// Correct!
return (
[<Comp1 />,
<Comp2 />]
)
Please note that the above will generate a warning: Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of 'YourComponent'.
This can be fixed by adding a key attribute to the components, if manually adding these add it like:
return (
[<Comp1 key="0" />,
<Comp2 key="1" />]
)
Here is some more information on keys:Composition vs Inheritance
The problem
Parse Error: Adjacent JSX elements must be wrapped in an enclosing tag
This means that you are trying to return multiple sibling JSX elements in an incorrect manner. Remember that you are not writing HTML, but JSX! Your code is transpiled from JSX into JavaScript. For example:
render() {
return (<p>foo bar</p>);
}
will be transpiled into:
render() {
return React.createElement("p", null, "foo bar");
}
Unless you are new to programming in general, you already know that functions/methods (of any language) take any number of parameters but always only return one value. Given that, you can probably see that a problem arises when trying to return multiple sibling components based on how createElement() works; it only takes parameters for one element and returns that. Hence we cannot return multiple elements from one function call.
So if you've ever wondered why this works...
render() {
return (
<div>
<p>foo</p>
<p>bar</p>
<p>baz</p>
</div>
);
}
but not this...
render() {
return (
<p>foo</p>
<p>bar</p>
<p>baz</p>
);
}
it's because in the first snippet, both <p>-elements are part of children of the <div>-element. When they are part of children then we can express an unlimited number of sibling elements. Take a look how this would transpile:
render() {
return React.createElement(
"div",
null,
React.createElement("p", null, "foo"),
React.createElement("p", null, "bar"),
React.createElement("p", null, "baz"),
);
}
Solutions
Depending on which version of React you are running, you do have a few options to address this:
Use fragments (React v16.2+ only!)
As of React v16.2, React has support for Fragments which is a node-less component that returns its children directly.
Returning the children in an array (see below) has some drawbacks:
Children in an array must be separated by commas.
Children in an array must have a key to prevent React’s key warning.
Strings must be wrapped in quotes.
These are eliminated from the use of fragments. Here's an example of children wrapped in a fragment:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
which de-sugars into:
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
Note that the first snippet requires Babel v7.0 or above.
Return an array (React v16.0+ only!)
As of React v16, React Components can return arrays. This is unlike earlier versions of React where you were forced to wrap all sibling components in a parent component.
In other words, you can now do:
render() {
return [<p key={0}>foo</p>, <p key={1}>bar</p>];
}
this transpiles into:
return [React.createElement("p", {key: 0}, "foo"), React.createElement("p", {key: 1}, "bar")];
Note that the above returns an array. Arrays are valid React Elements since React version 16 and later. For earlier versions of React, arrays are not valid return objects!
Also note that the following is invalid (you must return an array):
render() {
return (<p>foo</p> <p>bar</p>);
}
Wrap the elements in a parent element
The other solution involves creating a parent component which wraps the sibling components in its children. This is by far the most common way to address this issue, and works in all versions of React.
render() {
return (
<div>
<h1>foo</h1>
<h2>bar</h2>
</div>
);
}
Note: Take a look again at the top of this answer for more details and how this transpiles.
React 16.0.0 we can return multiple components from render as an array.
return ([
<Comp1 />,
<Comp2 />
]);
React 16.2.0 > we can return multiple components from render in a Fragment tag. Fragment
return (
<React.Fragment>
<Comp1 />
<Comp2 />
</React.Fragment>);
React 16.2.0 > you can use this shorthand syntax. (some older tooling versions don’t support it so you might want to explicitly write <Fragment> until the tooling catches up.)
return (
<>
<Comp1 />
<Comp2 />
</>)
If you do not wrap your component then you can write it as mentioned below method.
Instead of:
return(
<Comp1 />
<Comp2 />
);
you can write this:
return[(
<Comp1 />
),
(
<Comp2 />
) ];
it's very simple we can use a parent element div to wrap all the element
or we can use the Higher Order Component( HOC's ) concept i.e very useful for
react js applications
render() {
return (
<div>
<div>foo</div>
<div>bar</div>
</div>
);
}
or another best way is HOC its very simple not very complicated
just add a file hoc.js in your project and simply add these codes
const aux = (props) => props.children;
export default aux;
now import hoc.js file where you want to use, now instead of wrapping with div
element we can wrap with hoc.
import React, { Component } from 'react';
import Hoc from '../../../hoc';
render() {
return (
<Hoc>
<div>foo</div>
<div>bar</div>
</Hoc>
);
}
There is a rule in react that a JSX expression must have exactly one outermost element.
wrong
const para = (
<p></p>
<p></p>
);
correct
const para = (
<div>
<p></p>
<p></p>
</div>
);
React 16 gets your return as an array so it should be wrapped by one element like div.
Wrong Approach
render(){
return(
<input type="text" value="" onChange={this.handleChange} />
<button className="btn btn-primary" onClick= {()=>this.addTodo(this.state.value)}>Submit</button>
);
}
Right Approach (All elements in one div or other element you are using)
render(){
return(
<div>
<input type="text" value="" onChange={this.handleChange} />
<button className="btn btn-primary" onClick={()=>this.addTodo(this.state.value)}>Submit</button>
</div>
);
}
React components must wrapperd in single container,that may be any tag
e.g. "< div>.. < / div>"
You can check render method of ReactCSSTransitionGroup
Import view and wrap in View. Wrapping in a div did not work for me.
import { View } from 'react-native';
...
render() {
return (
<View>
<h1>foo</h1>
<h2>bar</h2>
</View>
);
}
Invalid: Not only child elements
render(){
return(
<h2>Responsive Form</h2>
<div>Adjacent JSX elements must be wrapped in an enclosing tag</div>
<div className="col-sm-4 offset-sm-4">
<form id="contact-form" onSubmit={this.handleSubmit.bind(this)} method="POST">
<div className="form-group">
<label for="name">Name</label>
<input type="text" className="form-control" id="name" />
</div>
<div className="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" className="form-control" id="email" aria-describedby="emailHelp" />
</div>
<div className="form-group">
<label for="message">Message</label>
<textarea className="form-control" rows="5" id="message"></textarea>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
)
}
Valid: Root element under child elements
render(){
return(
<div>
<h2>Responsive Form</h2>
<div>Adjacent JSX elements must be wrapped in an enclosing tag</div>
<div className="col-sm-4 offset-sm-4">
<form id="contact-form" onSubmit={this.handleSubmit.bind(this)} method="POST">
<div className="form-group">
<label for="name">Name</label>
<input type="text" className="form-control" id="name" />
</div>
<div className="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" className="form-control" id="email" aria-describedby="emailHelp" />
</div>
<div className="form-group">
<label for="message">Message</label>
<textarea className="form-control" rows="5" id="message"></textarea>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
</div>
)
}
Just add
<>
// code ....
</>
For Rect-Native developers. I encounter this error while renderingItem in FlatList. I had two Text components. I was using them like below
renderItem = { ({item}) =>
<Text style = {styles.item}>{item.key}</Text>
<Text style = {styles.item}>{item.user}</Text>
}
But after I put these tow Inside View Components it worked for me.
renderItem = { ({item}) =>
<View style={styles.flatview}>
<Text style = {styles.item}>{item.key}</Text>
<Text style = {styles.item}>{item.user}</Text>
</View>
}
You might be using other components but putting them into View may be worked for you.
I think the complication may also occur when trying to nest multiple Divs within the return statement. You may wish to do this to ensure your components render as block elements.
Here's an example of correctly rendering a couple of components, using multiple divs.
return (
<div>
<h1>Data Information</H1>
<div>
<Button type="primary">Create Data</Button>
</div>
</div>
)

React slow with multiple controlled text inputs

I have a form with multiple text inputs. I have them all set up as controlled inputs. When typing, there is a lag of up to several seconds for the new text to display in the field. Here is an example field:
<label>Event Name</label>
<input type="text"
placeholder="Title"
className="form-control"
name="title"
value={this.state.event.title}
onChange={this.handleChange} />
I can't figure out what's causing it to be so slow or what to do to fix it.
UPDATED: Here's the component, should be enough to show what's going on.
let CreateEventForm = React.createClass({
submit: function () {},
handleChange: function(e){
let value = e.target.value;
let name = e.target.name;
if(value === 'true'){
value = true;
}
if(value === 'false'){
value = false;
}
// If true/false toggle old
let oldState = this.state.event[name];
if(typeof oldState === 'boolean'){
value = !oldState;
}
// If is array
if(name.indexOf('[]') > -1){
name = name.replace('[]', '');
oldState = this.state.event[name];
var pos = oldState.indexOf(value);
if(pos > -1){
oldState.splice(pos, 1);
} else {
oldState.push(value);
}
value = oldState;
}
let event = this.state.event;
event[name] = value;
this.setState({event: event});
console.log(this.state.event);
},
getClasses(field, additionalClasses = []) {
// If a string is passed for additional class, make array
if(!Array.isArray(additionalClasses)){
additionalClasses = [additionalClasses];
}
let useDefaultColumns = additionalClasses.filter(function(className){
return className.indexOf('col-') > -1;
}).length === 0;
let hasError = function(){
let fields = Array.isArray(field) ? field : [field];
return fields.filter(function(field){
return !this.props.isValid(field);
}.bind(this)).length > 0;
}.bind(this)();
return classnames({
'col-sm-4': useDefaultColumns,
'form-group': true,
'has-error': hasError
}, additionalClasses);
},
render: function () {
return (
<form ref="eventForm" onSubmit={this.submit}>
<SavedModal isOpen={this.state.saved} reset={this.resetForm} style={this.state.modals.styles} />
<h3>Info</h3>
<div className="row">
<div className={this.getClasses('title')}>
<label>Event Name</label>
<input type="text" placeholder="Title"
className="form-control"
name="title"
value={this.state.event.title}
onChange={this.handleChange} />
{this.renderHelpText(this.props.getValidationMessages('title'))}
</div>
</div>
<div className="row">
<div className={this.getClasses('type')}>
<label>Event Type</label>
<select name="type"
className="form-control"
value={this.state.event.type}
onChange={this.handleChange}
onBlur={this.props.handleValidation('type')}>
<option value="">Select Event Type…</option>
{this.state.calendarTypes.map(function (type, key) {
return <option value={type.name} key={key}>{type.name}</option>
})}
</select>
{this.renderHelpText(this.props.getValidationMessages('type'))}
</div>
</div>
<h3>Duration</h3>
<div className="row">
<div className="form-group col-sm-2">
<input type="checkbox" name="allDay" checked={this.state.event.allDay} onChange={this.handleChange}/> All Day
</div>
</div>
<div className="row">
<div className="form-group col-sm-2">
<input type="checkbox" name="repeats" checked={this.state.event.repeats} onChange={this.handleChange}/> Repeats…
</div>
<br/><br/>
</div>
<h3>Location</h3>
<div className="row">
<div className={this.getClasses('location')}>
<select name="location"
className="form-control"
value={this.state.event.location}
onBlur={this.props.handleValidation('location')}
onChange={this.handleChange}>
<option value="">Select a Location…</option>
{this.state.locations.map(function (location, key) {
return (
<option value={location.name} key={key}>{location.name}</option>
);
})}
</select>
{this.renderHelpText(this.props.getValidationMessages('location'))}
</div>
</div>
<h3>Description</h3>
<div className="row">
<div className={this.getClasses('description')}>
<label>Write a description:</label>
<textarea className="form-control"
name="description"
value={this.state.event.description}
onChange={this.handleChange}
onBlur={this.props.handleValidation('description')}
rows="10"></textarea>
{this.renderHelpText(this.props.getValidationMessages('description'))}
</div>
</div>
<h3>Event Details</h3>
<div className="row">
<div className={this.getClasses('fee')}>
<label>Fee:</label>
<input type="text"
className="form-control"
name="fee"
value={this.state.event.fee}
onChange={this.handleChange}
onBlur={this.props.handleValidation('fee')}/>
{this.renderHelpText(this.props.getValidationMessages('fee'))}
</div>
</div>
<div className="row">
<div className="col-sm-12">
<button className="btn btn-primary" type="submit">
Create Event
</button>
</div>
</div>
</form>
);
}
});
I had a similar situation and my solution was to disable React Dev Tools. They were affecting input fields somehow. The problem is it's not enough to refresh a page if you have clicked React Dev Tools tab. They are still affecting your inputs. You have to open new page to stop them. You can also remove them from Chrome completely but I don't recommend doing this 'cause they are useful. :)
There are many possible reasons for this to happen. I faced the similar issue and filtered the main cause to:
Large State, so it takes up sometime
React Dev Tool / Use of non minified react
Mutating state data
Whatever may be the reason, I found a quick fix for this. If you just want to store it to the state but not using it for live rendering. Then you can safely replace the 'onChange' to 'onBlur'. This has no dealay and lag. If you know any other case where this will not work, do let me know!
Change the code as follow:
<label>Event Name</label>
<input
type="text"
placeholder="Title"
className="form-control"
name="title"
value={this.state.event.title}
onBlur={this.handleChange} />
Here is an example for an input pattern, Plug in or pattern for dealing with large forms in React?. The main thing is to have your input as a component that passes changes to the parent but doesn't update from the props if they are the same.
My problem was that my state object was complex and thus causing rendering issues. My solution was to manage the state of the notes in my component, then on blur update the container state.
const { data, onEdit } = props;
const { containerNotes } = data;
const [notes, setNotes] = useState('');
useEffect(
() => {
setNotes(containerNotes);
},
[containerNotes],
);
const onChangeNotes = () => ({ target: { value } }) => {
setNotes(value);
};
const onBlurNotes = (prop) => ({ target: { value } }) => {
const newData = update(data, {
[prop]: { $set: value },
});
onEdit(newData);
};
return (
<input
type="text"
placeholder="Title"
name="title"
value={notes}
onChange={onChangeNotes()}
onBlur={onBlurNotes('containerNotes')}
/>
)
Here is a fix I found. It sets the parent state in onBlur. Please vet this
import React, { useState } from 'react';
import MUIField from '#material-ui/core/TextField';
import _ from 'lodash';
export default (props) => {
const [value, setValue] = useState(props.defaultValue);
const prop = _.omit(props, ['onChange', 'value', 'defaultValue']);
return (
<MUIField {...prop} value={value}
onChange={e => { setValue(e.target.value); }}
onBlur={() => {
props.onChange({ target:{ value }});
}}/>);
};
Seeing as this still gets responses, thought I'd update this and close it. Calling handleChange within onChange would cause the entire form to rerender on every change, thereby slowing it down. If you had a simple form you could just update onBlur instead or create individual input components so the rerender would be isolated to that one input. Personally, I use and would recommend using Formik for building forms as it abstracts this complexity for you.
Apparently React-Hook-Form is great at minimising re-renders, and probably optimise performance. I have yet to try it though.
I've had redux logger middleware take enough time to create this input lag. So, try disabling that as well.
useRef instead, it doesn't re-render
const App = () => {
const textRef = useRef();
const showRefContent = () => {
console.log(textRef.current.value);
};
return (
<div className="App">
<TextField inputRef={textRef} />
<button onClick={showRefContent}>Click</button>
</div>
);
}

Categories