rendering an additional pop up dialog on button click React - javascript

I am having issues with rendering a pop up loading screen. So assume i have a imported component called ( LoadingDialog ) and i want it to render when the state property, loading is true. When the user clicks a button on the current component, it triggers an api call which also changes the loading state to true, thus rendering the loading dialog.
I understand I can use conditional rendering to achive this, eg:
if(this.state.loading){
return (
<div>
<LoadingDialog />
</div>
)
}
else{
return(
<div> OTHER UI ELEMENTS </div>
)
but now i have a problem because, when my loadingDialog is rendered, my other ui (text area, background card, button ) all disappear, which is the opposite of what im trying to achieve. With this approach, i can only display my actual ui elements or the loading dialog.
I've tried separating the other ui elements into a separate container but it doesn't help as i need to call the api on click of an button and the entire problem i'm having now occurs in that child container.
I've also tried the above approach with passing a parent on click method as a prop and calling that when the button is clicked but somehow ended up with a recursive loop of the parent/child component
Heres my actual code:
if(this.state.loading){
return (
<div>
<LoadingDialog />
</div>
)
}
else{
return (
<div>
<Card className="center card">
<div className="row">
<div class="column" >
<TextField
id="outlined-name"
name="searchContent"
onChange={this.handleChange}
value={this.state.searchContent}
label="Name"
variant="outlined"
/>
</div>
<div className="column">
<Button
variant="outlined"
color="primary"
onClick={this.handleClick}
>
Search
</Button>
</div>
</div>
</Card>
</div>
);
}
and this is my handle click function:
handleClick = (event, name) => {
this.setState({loading : true})
fetch(uri)
.then(res => res.json())
.then(data => {
console.log(data);
this.setState({loading : false})
});
};
As i said before, I tried separating the UI bit on the else block to a different component but the problem still persisted. To summarise it again,
I can only render my actual ui or a popup box but not both at any given time.
I want to be able to render both at the same time, if needed.
I am very new to react and staying away from the likes of redux, hooks etc.

SOLVED Thanks to Chris G
So the issue was easily fixed by using a logical and operator to check if loading is true or false, like so {this.state.loading && <LoadingDialog />}
eg.
render(){
return(
<div>
{this.state.loading && <LoadingDialog />}
<div>
//REST OF THE STUFF THAT SHOULD BE RENDERED REGARDLESS
</div>
</div>
)
}

Related

react.js image onClick not working for some reason, while same function works for another div

i am making Faq questions in react and my question div displays text when clicked on whole body as shown below:
Here is my react code. div with class "question-body" works with onclick but when I click the Plus img it has no action,any mistakes?
export default function Question({data}){
let imagid = 'img'+data.id;
function toggle(){
document.getElementById(data.id).classList.toggle('question-p') ;
document.getElementById(imagid).classList.toggle('rotateimg');
}
return(
<div className="column width100 question">
<div onClick={() => toggle()} className="question-body">
<div className="flex-between">
<label className="faq-question">{data.question}</label>
<img onClick={() => toggle()} id={imagid} className='togglequestion' src={togglequestion}></img>
</div>
<p id={data.id} className='none' dangerouslySetInnerHTML={{ __html: data.answer }}></p>
</div>
</div>
)
}
even though the answer is pretty simple here, it would require you to redo most of your code.
In order to be working with React.js effectively, you need to learn the basics (component state management,... ) - https://reactjs.org/docs/getting-started.html
I highly encourage you not to use dangerouslySetInnerHTML as it can expose your code to easy exploits.
With React.js, you don’t really want to use Document Object Model APIs (via toggle function)

onClick Removing data from components

So basically I have two components. In Component 1 there is an img. In component 2 there is a button. I need when i press the button img disappears and also button disappears.
const Icons=()=> {
return (
<div className="_icons">
<div className="icons__Top">
<img src="./icons/-48.png" alt="Twitter"/>
<figcaption>Whatever</figcaption>
</div>
</div>
const Button= () => {
return (
<div className="button">
<Button variant="outlined" className="button__rightpage" >REMOVE</Button>
<caption className="text" ></caption>
</div>
)
}
There can be more than one solution,
1)I think you need to have one parent component for both components and make one state in parent component and then pass it in icon component and give it to img and default make it to false and then from button component change that state to true.
2)without parent component -> You can also use context api to change value directly from button component and hide img.

How to progressively render react components?

I'm working with lists that will likely be in the range of 500 up to maybe 5000 items. Each item in the list will show as a component, like so:
List
render() {
return (
<div className="ItemList">
<Info items={this.props.items} />
<ul>
{this.props.items.map( item =>
<Item item={item} key={item._id} refresh={this.props.refresh} />
)}
</ul>
</div>
);
}
Item
render() {
var item = this.props.item;
return (
<li onClick={() => this.setState({showInfo: !this.state.showInfo})}
className="Item">
<h3 className={this.state.showInfo ? "item-title-bar active" : "item-title-bar"}>
<div>
<div className="item-category">
{item.category}
</div>
<div className="item-name">
{item.name}
</div>
</div>
</h3>
{this.state.showInfo &&
<ItemInfo item={this.props.item} refresh={this.props.refresh} />
}
</li>
);
}
Once one of these lists gets up to around 1000 items, it's noticeably slow when I click to show a different list. Perf tools are showing me 90-150 ms for displaying this list at 1000 or 2000 items. Not sure I can get around that as long as I'm rendering them.
So, what I'm trying to do:
Can I let the initial items update, then render others in the background, while the app remains responsive?
How can I show initial items, then load more as the user scrolls down the page?
If neither option works, I'll probably try to load a few, then add a show more or show all button at the bottom of the list. Want to make this as seamless as possible though, open to other suggestions as well.
react-virtualized would be my first choice when dealing with a virtual list. Lot of examples here: https://bvaughn.github.io/react-virtualized/#/components/List
Pretty simple if you know the heights of the items ahead of time, but can use the CellMeasurer component if you don't.

Learning React.js

I am trying to learn React. I already have a good grasp of javascript. I am trying to learn by creating a small app that is basically a task manager. In my case it's for grocery related items. I have a fiddle created here. Could you please take a look at how I composed the react code and let me know if this is the best approach to building with components/classes? You can see that I have nested components. I am not sure if there is a better way of doing this.
Finally, I wan't a new "add-item-row" created every time a user clicks on the big blue Add button. Right now one is showing be default but I don't want any showing by default. I want one created (add-item-row, div) only when a user clicks on the Add button.
Here is the fiddle.
https://jsfiddle.net/j0mpsbh9/4/
<div id="app" class="container">
<script type="text/babel">
var AddItemWrapper = React.createClass({
render: function() {
return (
<div>
<div className="row">
<AppTitle />
<AddItemForm />
</div>
<AddItemRow />
</div>
);
}
});
var AppTitle = React.createClass({
render: function() {
return (
<div>
<h1>Grocery List</h1>
</div>
);
}
});
var AddItemForm =React.createClass({
render: function() {
return (
<div>
<div className="col-sm-6 col-lg-6">
<div className="form-group">
<label htmlFor="enter-grocery-item" className="sr-only">Enter Grocery Item</label>
<input type="text" className="form-control" id="enter-grocery-item" placeholder="Enter Grocery Item" />
</div>
</div>
<div className="col-sm-6 col-lg-6">
<button type="button" id="add" className="btn btn-block btn-info">Add <span className="glyphicons circle_plus"></span></button>
</div>
</div>
);
}
});
var AddItemRow =React.createClass({
render: function() {
return (
<div className="add-item-row">
<div className="row">
<div className="col-sm-12 grocery-items">
<div className="col-sm-6">
<div className="form-group">
<label htmlFor="grocery-item" className="sr-only">Grocery Item</label>
<input type="text" className="form-control" id="grocery-item" placeholder="" />
</div>
</div>
<div className="col-sm-6 center">
<button type="button" className="btn btn-blockx btn-warning"><span className="glyphicons pencil"></span></button>
<button type="button" className="btn btn-blockx btn-lgx btn-danger"><span className="glyphicons remove"></span></button>
<button type="button" className="btn btn-blockx btn-lgx btn-success"><span className="glyphicons thumbs_up"></span></button>
</div>
</div>
</div>
</div>
);
}
});
ReactDOM.render(
<AddItemWrapper />,
document.getElementById('app')
);
</script>
</div>
You have a good start here! Your component hierarchy is organized in a sensible way. However you are missing any kind of interactivity or internal state.
The main way you make React components interactive is by using state plus event callbacks which modify said state. "State" is pretty self explanatory - it describes values inherent to how the components looks and behaves, but which change over time. Every time a React component's state is altered (with this.setState()) that component will re-render (literally by re-running the render() function) to reflect the changes.
First let's edit your AddItemWrapper class to keep track of some internal state when it is first mounted. We know that you want to have multiple rows of data, so let's give it an empty array to store future information about rows:
getInitialState(){
return {rows: []};
},
Now instead of rendering a single AddItemRow directly, we'll render a dynamic set of rows that is based on the current component state. Array.map() is perfect for this and a common use case in React:
{this.state.rows.map(function(ea, i){
return <AddItemRow initialItemName={ea} key={ea + "-" + i} />
})}
Basically what that does is take every entry in the array AddItemWrapper.state.rows array and renders it as an AddItemRow component. We give it two properties, initialItemName and key. "initialItemName" will just tell the child component what its name was when it was first added, and "key" is a unique string that allows React to differentiate components from their siblings.
Now we've set up AddItemWrapper to properly render rows based on its internal state. Next we have to modify AddItemForm so that it will react to user input and trigger new rows being added.
In AddItemForm, first we need to add a "ref" to the input text box. This is so that React can identify and read data from this HTML element after it is rendered:
<input ref={function(el){this.inputElement = el;}.bind(this)} ... />
Then give the button element a callback that will trigger when it's clicked:
<button onClick={this.handleClick} ... />
Finally write the callback handler itself:
handleClick(){
this.props.onAdd(this.inputElement.value);
this.inputElement.value = "";
}
Notice how this callback is calling this.props.onAdd()? That means we need to pass in a callback function from the parent (AddItemWrapper) to this component to use. This is how we communicate between parents and children in React: pass a function from a parent to a child which will be triggered from within the child, but will effect the parent.
In AddItemWrapper we make sure AddItemForm has access to the callback function:
AddItemForm onAdd={this.onAdd} />
And then we write the callback function itself:
onAdd(newItem){
var newRows = this.state.rows.slice();
newRows.push(newItem);
this.setState({rows: newRows});
}
Notice how we're copying the old array held in state (using Array.slice()), push a new item into the new array, and then update state with the new array? Never mutate state directly; ALWAYS copy it, modify the copy, and then update state with the new copy.
Almost done. We've created a way for AddItemWrapper to render its rows, and a way for AddItemForm to create new rows. Now we edit AddItemRow to render in a way that maintains its own internal state too.
First make sure it initializes its own state when it's mounted. We'll have it keep track of a string value, which initially is the same as what the user entered into the text box when they pressed "Add", but because it's kept in AddItemRow.state it can be modified later by the user:
getInitialState() {
return {itemName: this.props.initialItemName}
}
Now that the row name is kept in the component state, we can render it in the HTML like this:
<input value={this.state.itemName} ... />
Here's what it looks like when you put it all together!
There are obviously more features that you would want to add, such as letting the user edit, move, or delete a row entry. I'll leave those exercises up to you. I highly recommend you read through all of the official documentation as well as do a few tutorials to get your head in the game. It's obvious that you have a bit of experience under your belt given what you had so far, but getting the hang of how state, render(), and callbacks work takes some practice. Good luck!

How to avoid extra wrapping <div> in React?

Today I have started learning ReactJS and after an hour faced with the problem..
I want to insert a component which has two rows inside a div on the page.A simplified example of what I am doing below.
I have an html:
<html>
..
<div id="component-placeholder"></div>
..
</html>
Render function like this:
...
render: function() {
return(
<div className="DeadSimpleComponent">
<div className="DeadSimpleComponent__time">10:23:12</div >
<div className="DeadSimpleComponent__date">MONDAY, 2 MARCH 2015</div>
</div>
)
}
....
And below I am calling render:
ReactDOM.render(<DeadSimpleComponent/>, document.getElementById('component-placeholder'));
Generated HTML looks like this:
<html>
..
<div id="component-placeholder">
<div class="DeadSimpleComponent">
<div class="DeadSimpleComponent__time">10:23:12</div>
<div class="DeadSimpleComponent__date">MONDAY, 2 MARCH 2015</div>
</div>
</div>
..
</html>
The problem that I am not a very happy that React forcing me to wrap all in a div "DeadSimpleComponent". What is the best and simple workaround for it, without explicit DOM manipulations?
UPDATE 7/28/2017: Maintainers of React added that possibility in React 16 Beta 1
Since React 16.2, you can do this:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
This requirement was removed in React version (16.0), so now you are able to avoid that wrapper.
You can use React.Fragment to render a list of elements without creating a parent node, official example:
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
More here: Fragments
Update 2017-12-05:
React v16.2.0 now fully supports rendering of fragments with improved support for returning multiple children from a components render method without specifying keys in children:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
If you are using a React version prior to v16.2.0, it is also possible to use <React.Fragment>...</React.Fragment> instead:
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
Original:
React v16.0 introduced returning an array of elements in render method without wrapping it in a div: https://reactjs.org/blog/2017/09/26/react-v16.0.html
render() {
// No need to wrap list items in an extra element!
return [
// Don't forget the keys :)
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}
At the moment, a key is required for each element to avoid the key warning but this could be changed in future releases:
In the future, we’ll likely add a special fragment syntax to JSX that
doesn’t require keys.
You can use:
render(){
return (
<React.Fragment>
<div>Some data</div>
<div>Som other data</div>
</React.Fragment>
)
}
For further details refer to this documentation.
Use [], instead of ()'s to wrap the entire return.
render: function() {
return[
<div className="DeadSimpleComponent__time">10:23:12</div >
<div className="DeadSimpleComponent__date">MONDAY, 2 MARCH 2015</div>
]
}
I created a component to wrap child components without a DIV. It's called a shadow wrapper: https://www.npmjs.com/package/react-shadow-wrapper
This is still required, BUT React now make sure to create elements without creating an additional DOM element.
The extra wrapping needed (normally with a parent div) because Reacts createElement method require a type parameter which is either a tag name string (such as 'div' or 'span'), a React component type (a class or a function). But this was before they introduce React Fragment.
Refer this NEW api doc for createElement
React.createElement : Create and return a new React element of the given type. The type argument can be either a tag name string (such as 'div' or 'span'), a React component type (a class or a function), or a React fragment type.
here is the official example, Refer React.Fragment.
render() {
return (
<React.Fragment>
Some text.
<h2>A heading</h2>
</React.Fragment>
);
}
I know this question has been answered, you can of course use React.Fragment which doesn't create a node but let's you group stuff like a div.
Additionally if you want to have fun you can implement (and learn lots of things) a React mode that removes the extra div's and for this I really want to share a great video on how you can do it on the react code base itself.
https://www.youtube.com/watch?v=aS41Y_eyNrU
This is of course not something that you would do in practice but it's a good learning opportunity.
You won't be able to get rid of that div element. React.render() needs to return one valid DOM node.
Here is one way to render "transculent" components:
import React from 'react'
const Show = (props) => {
if (props.if || false) {
return (<React.Fragment>{props.children}</React.Fragment>)
}
return '';
};
----
<Show if={yomama.so.biq}>
<img src="https://yomama.so.biq">
<h3>Yoamama</h3>
<Show>
There is workaround too. The below block code generates fragment without the need of React.Fragment.
return [1,2,3].map(i=>{
if(i===1) return <div key={i}>First item</div>
if(i===2) return <div key={i}>Second item</div>
return <div key={i}>Third item</div>
})

Categories

Resources