2 different ways to create React component - javascript

I was following a react tutorial and this is the example code the author has given to create a basic React component:
const React = require('react')
const ReactDOM = require('react-dom')
const App = () => {
return (
<div className='app-container'>
<h1>Hello</h1>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
He claim it's ES6.
But then I saw another way to create component.
class App extends React.Component {
render(){
return <h1>Hello</h1>;
}
}
hmm I'm confused now. Is there any standard way of doing things in react?

In React you can create the so-called stateful and stateless functional components. Stateless components are simple reusable components which do not need to maintain state. Here is a short demo (http://codepen.io/PiotrBerebecki/pen/yaoOKv) showing you how you can create them and how they can access props passed from the parent (stateful component).
A simple example may be a theoretical App stateful component on Facebook.com. It could maintain state to track if user is logged in or logged out. Then in its render() method it would show a LoginLogout stateless button component passing to it the current state. The LoginLogout stateless component would then show either:
'Log In' text if user is not logged in, or
'Log Out' text if user is logged in.
You can learn more about stateful vs stateless components here: ReactJS difference between stateful and stateless and here React.createClass vs. ES6 arrow function
// Stateful component
class FacelookApp extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: false
};
}
receiveClick() {
this.setState({
isLoggedIn: !this.state.isLoggedIn
});
}
render() {
return (
<div>
<h4>Welcome, I'm a stateful parent called Facelook App</h4>
<p>I maintain state to monitor if my awesome user logged
in. Are you logged in?<br />
<b>{String(this.state.isLoggedIn)}</b>
</p><br />
<p>Hi, we are three stateless (dumb) LoginLogout buttons
generated using different ES6 syntax but having the same
functionality. We don't maintain state. We will tell
our parent if the user clicks on us. What we render is
decided by the value of the prop sent to us by our parent.
</p>
<LoginLogout1 handleClick={this.receiveClick.bind(this)}
isLoggedIn={this.state.isLoggedIn}/>
<LoginLogout2 handleClick={this.receiveClick.bind(this)}
isLoggedIn={this.state.isLoggedIn}/>
<LoginLogout3 handleClick={this.receiveClick.bind(this)}
isLoggedIn={this.state.isLoggedIn}/>
</div>
);
}
}
// Stateless functional components
// created in 3 equally valid ways
const LoginLogout1 = (props) => {
return (
<div>
<button onClick={() => props.handleClick()}>
LoginLogout v1 --- {props.isLoggedIn ? 'Log Out' : 'Log In'}
</button>
</div>
);
};
// or
const LoginLogout2 = ({handleClick, isLoggedIn}) => (
<div>
<button onClick={() => handleClick()}>
LoginLogout v2 --- {isLoggedIn ? 'Log Out' : 'Log In'}
</button>
</div>
);
// or
const LoginLogout3 = ({handleClick, isLoggedIn}) => {
return (
<div>
<button onClick={() => handleClick()}>
LoginLogout v3 --- {isLoggedIn ? 'Log Out' : 'Log In'}
</button>
</div>
);
};
ReactDOM.render(
<FacelookApp />,
document.getElementById('app')
);

Both of them are "equally standard".
Though the syntax for the second case is off. It should read class App extends React.Component {
The second approach is the most generic one, because it allows for state, extra functionality besides the render and Component lifetime methods etc. But when you have "functional" components, which just display something based on their props, you have the first approach as a shorthand for a class with just the render method. When calling .render React knows how to deal with the two cases.

const App = () => {
return (
<div className='app-container'>
<h1>Hello</h1>
</div>
)
}
is called "stateless function component", which can not have state
https://facebook.github.io/react/docs/reusable-components.html#stateless-functions
the another one is a normal component.

Related

How to share state between child component (siblings) in ReactJS?

I would like to pass state to a sibling or even a grandparent whatever.
I have 3 components. Inside Header, I have a button with an onClick function to toggle a Dropdown Menu inside Navigation. And by the way, I would like to pass the same state to AnotherComponent.
How to pass state (such as isDropdownOpened) from Header to Navigation and AnotherComponent?
<div>
<Header />
<Navigation />
<div>
<div>
<div>
<AnotherComponent />
</div>
</div>
</div>
</div>
You have different approaches to address this situation.
Keep the state in the top component and pass it to children through props
Use a state container to keep and share your application state among components (e.g. https://redux.js.org/)
Use the new React Context feature. Context provides a way to pass data through the component tree without having to pass props down manually at every level.
That's the exact reason why "React Hooks" have been developed (and hyped by the community 😉), but don't use them yet in production, they are still in early development (alpha) and their specification/implementation might be changed!
You problem can be solved using the awesome “React Context“ API which allows to pass data to components no matter how deep they are nested in the tree.
To get to know to context read the extensive documentation linked above. I'll only explain a small and quick example here:
Create a context component and export the consumer
App.jsx
import React from "react";
// The initial value can be anything, e.g. primitives, object, function,
// components, whatever...
// Note that this is not required, but prevebents errors and can be used as
// fallback value.
const MyContext = React.createContext("anything");
// This component is the so called "consumer" that'll provide the values passed
// to the context component. This is not necessary, but simplifies the usage and
// hides the underlying implementation.
const MyContextConsumer = MyContext.Consumer;
const someData = { title: "Hello World" };
const App = ({ children }) => (
<MyContext.Provider value={someData}>{children}</MyContext.Provider>
);
export { MyContextConsumer };
export default App;
Import the created consumer in any component and use the provided value
AnotherComponent.jsx
import React from "react";
import { MyContextConsumer } from "./App";
const AnotherComponent = () => (
<div>
<MyContextConsumer>{({ title }) => <h1>{title}</h1>}</MyContextConsumer>
</div>
);
export default AnotherComponent;
Render the app with both context components
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import AnotherComponent from "./AnotherComponent";
const Root = () => (
<App>
<AnotherComponent />
</App>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<Root />, rootElement);
The component will render a level 1 heading with the "Hello World" text.
How to pass state (such as isDropdownOpened) from Header to Navigation and AnotherComponent, please ?
You hold the state in an ancestor of Header and pass that state to Haeader, Navigation, and AnotherComponent as props. See State and Lifecycle and Lifting State Up in the documentation.
Example:
const Header = props => (
<div>
<span>Header: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
const Navigation = props => (
<div>
<span>Navigation: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
const AnotherComponent = props => (
<div>
<span>AnotherComponent: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
isDropdownOpened: false
};
}
componentDidMount() {
setInterval(() => {
this.setState(({isDropdownOpened}) => {
isDropdownOpened = !isDropdownOpened;
return {isDropdownOpened};
});
}, 1200);
}
render() {
const {isDropdownOpened} = this.state;
return (
<div>
<Header isDropdownOpened={isDropdownOpened} />
<Navigation isDropdownOpened={isDropdownOpened} />
<div>
<div>
<div>
<AnotherComponent isDropdownOpened={isDropdownOpened} />
</div>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<Wrapper />,
document.getElementById("root")
);
<div id="root"></div>
<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>
There are some other options, which Arnaud usefully provides in his answer.
Like how TJ Said, use the state of the parent component. That way one state is shared by all the sub components, which is what you wanted I presume.
class ExampleParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isDropdownOpened: false
}
}
toggleDropdown() {
this.setState({
isDropdownOpened: !isDropdownOpened
});
}
render() {
return (
<div>
<Header open={isDropdownOpened} toggleDropdown={ this.toggleDropdown }/>
<Navigation open={ isDropdownOpened}/>
<div>
<div>
<div>
<AnotherComponent open={ isDropdownOpened} />
</div>
</div>
</div>
</div>
);
}
class Header extends React.Component {
render() {
return (
<div>
<button onClick={ this.props.toggleDropdown }>TOGGLE ME</button>
{ isDropdownOpened && (
<h1> DROPPED </h1>
}
</div>
);
}
}
You can only use this.state.variableName to access
<ChildComponent data={this.state.name} />
And to pass functions
<ChildComponent data={this.HandleChange} />
First Send the data from the first child to the common parent using callback
function and then send that received data (stored in state in parent component)
to the second child as props.
you can also read this article - https://www.pluralsight.com/guides/react-communicating-between-components

Can we pass setState as props from one component to other and change parent state from child component in React?

class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
this.setState=this.setState.bind(this)
}
render() {
return (
<div>
<Hello name={this.state.name} />
<p>
Start editing to see some magic happen :)
</p>
<Child {...this}/>
</div>
);
}
}
child Component
var Child=(self)=>{
return(
<button onClick={()=>{
self .setState({
name:"viswa"
})
}}>Click </button>
)
here I am binding the setState function and send this as props to child component.This will change state of parent from child.Is this proper way?
But this is simple than passing function.If I want change many state change. this will be simple and faster way compare to passing function right ?
The correct way to do this is as simple as yours and does not violate best practices:
class App extends Component {
state = {
name: 'React',
};
render() {
return (
<div>
<Hello name={this.state.name} />
<p>
Start editing to see some magic happen :)
</p>
<Child onClick={() => this.setState({name: 'viswa'})}/>
</div>
);
}
}
const Child=({onClick})=>(
<button onClick={onClick}>Click</button>
);
You shouldn't pass the setState directly, as the setState function will not change the state immediately.
as the documents said:
Think of setState() as a request rather than an immediate command to
update the component. For better perceived performance, React may
delay it, and then update several components in a single pass. React
does not guarantee that the state changes are applied immediately.
setState() does not always immediately update the component. It may batch or defer the update until later. So you'd better to manage the calling of setState function together as there may have competing of mutating parent's state. It is not recommended to pass this function to another component.
Encapsulating the calling of setState function in one class makes your code stronger.
As #yBrodsky said, you should rather pass down a function which does mutate the state. Here is an example:
class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
this.update=this.update.bind(this);
}
update(nextState) {
this.setState(nextState);
}
render() {
return (
<Child updateParent={this.update} />
);
}
}
const Child = ({updateParent}) => (
<button onClick={() => updateParent({name: 'Foo bar'})}>Click</button>
);
You now have full control over the state of the Parent in the child. Just pass the object to be shallowly merged into the parent state. In this example, name in App will change to Foo bar on button click.
i guess in functional components its possible to send your setState to childrens and change parent state in them but not in class base components

React router pass callback with current route to child component

I am using react with react router. Some of my components have sub-routes defined so I want to pass them a callback that enables to returning to a specific route/component. I want to avoid passing a string to a specific route (because of the dependency when routing changes happen in the code). So i prefer passing a callback and populating it with the value of match.url.
But this does not work: Instead of passing the value, match.url always refers to the current route.
Parent component (simplified):
export class ParentComponent extends React.Component {
render() {
const { history, match, contentId } = this.props;
return (
<div>
<div>
<div>Block 1</div>
<div>Block 2</div>
<div>Block 3</div>
</div>
{contentId && <MyChildComponent content={contentId} goBack={() => history.push(match.url)} />}
</div>
);
}
}
My child component (simplified):
export class MyChildComponent extends React.PureComponent {
render() {
return (
(
<React.Fragment>
<div role="dialog" onClick={this.props.goBack} />
</React.Fragment>),
);
}
}
My router:
const Routes = () => (
<Router history={createBrowserHistory()}>
<div>
<Route path="/result/:contentId?" component={ParentComponent} />
</div>
</Router>
);
So when I go to /result I see - as expected - all but the child component. When navigating to /result/someId I see the child component but the goBack only refers to the current page instead of the previous one.
constructor(props){
super(props);
this.goBack = this.goBack.bind(this);
}
goBack(){
this.props.history.goBack(); // You're not calling it from history
}
.....
<button onClick={this.goBack}>Go Back</button>
I think that you are using push to navigate to another route. So when you do history.push('/result/someId') you are adding another entry to history stack so goBack will navigate to the previous entry in the stack which is /result. It works as if you were a regular website and clicked a link - you could still go back even if what had changed was some dynamic parameter.
If you don't want to add up to history stack use - history.replace('/result/someId')
See navigating with history.
I figured out my core-problem was that I needed at least one part of the child routes in the parent component. This lead to changing path props also in the parent component when child-routes were changing.
My solution: Store the current location in the constructor of the parent component and pass this as prop to child components to refer back. It works but has the drawback that one can not directly access child component routes because they will not refer back to the right parent path. For my use case this is fine but improvements are welcome.
Parent component
export class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.location = this.props.location.pathname;
}
render() {
return (
{contentId && <MyChildComponent content={contentId} goBack={() =>
history.push(this.location)} />}
)
}

React - toggle in stateless component

I am trying to toggle visiblity of a div in a stateless component like this:
const playerInfo = (props) => {
let isPanelOpen = false;
return (
<div onClick={() => isPanelOpen = !isPanelOpen }>Toggle</div>
{isPanelOpen && <div className="info-panel">
{props.children}
</div>}
);
};
I see that the value of isPanelOpen changes to true, but the panel is not being shown. I assume that is because this is the stateless function that doesn't get called again, so once we return the jsx it will have the value of false, and won't update it later.
Is there a way of fixing this, and avoiding of pasing this single variable as props through 4 more parent stateless components?
You can't tell React to re-render the UI by assigning new value directly to the variable (in your case you did isPanelOpen = !isPanelOpen).
The correct method is to use setState.
But you cannot do it in a stateless component, you must do it in a stateful component, so your code should looks like this
import React, {Component} from 'react';
class playerInfo extends Component {
constructor(props){
super(props);
this.state = {
isPanelOpen: false
}
}
render() {
return (
<div onClick={() => this.setState({isPanelOpen: !this.state.isPanelOpen})}>Toggle</div>
{this.state.isPanelOpen && <div className="info-panel">
{this.props.children}
</div>}
);
}
}
Explanation
Remember two things:
1) Your UI should only bind to this.state.XXXX (for stateful component) or props.XXX (for stateless component).
2) The only way to update UI is by calling setState() method, no other way will trigger React to re-render the UI.
But... how do I update stateless component since it doesn't have the setState method?
ONE ANSWER:The stateless component should be contained in another stateful component.
Example
Let's say your stateless component is called Kid, and you have another stateful component called Mum.
import React, {Component} from 'react';
class Mum extends Component {
constructor(props){
super(props);
this.state = {
isHappy: false
}
}
render() {
return (
<div>
<button onClick={() => this.setState({isHappy: true})}>Eat</button>
<Kid isHappy={this.state.isHappy}/>
</div>
);
}
}
const Kid = (props) => (props.isHappy ? <span>I'm happy</span> : <span>I'm sad</span>);
You can do this by using useState hooks like this:
import { useState } from "react";
function playerInfo () {
const [panel, setPanel] = useState(false);
function toggleButton () {
if(!panel) setPanel(true);
else setPanel(false);
}
return (
<div>
<button onClick={toggleButton}>Toggle</div>
panel ? {this.props.children} : null;
</div>}
);
};
export default playerInfo;
I assume that is because this is the stateless function that doesn't get called again
Basically, the only way to re-render component is to change state or props. :)
So when you change a local variable, React doesn't get notified about it and doesn't start reconcilation.
You can do this with native Javascipt otherwise in React you can not do this with stateless Component :)
const playerInfo = (props) => {
let isPanelOpen = false;
return ( <
div onClick = {
() => {
if (document.getElementsByClassName("info-panel")[0].style.display == 'none') {
isPanelOpen = true;
document.getElementsByClassName("info-panel")[0].style.display = '';
} else {
isPanelOpen = false;
document.getElementsByClassName("info-panel")[0].style.display = 'none';
}
}
} > Toggle < /div> <
div className = "info-panel" > {
this.props.children
} <
/div>
);
};
As of version 16.8 of React you can handle this for stateless components using a hook - in this case useState. In it's simplest form it can be implemented like this:
import React, { useState } from 'react';
const PlayerInfo = (props) => {
const [showPanel, togglePanel] = useState(false);
return (
<div onClick={() => togglePanel(!showPanel) }>Toggle</div>
{showPanel && (
<div className="info-panel">
{props.children}
</div>
)}
</div>
);
};
export default PlayerInfo;
A functional component will not re-render unless a previous parent's state changes and passes down updated properties that propagate down to your functional component. You can pass an onClick handler in from a stateful parent and call this from your stateless component when its onClick is triggered. The parent will control the toggling of the display and pass it in as a prop to the child (see snippet below).
To architect this, you should determine if your HOC (higher order component) should be in charge of UI state. If so, then it can make the determination if its child component should be in an open state or not and then pass that as a property to the child state. If this is a component that should open and close independent of the world around it, then it should probably have its own state. For example, if you are making a tabbed widget, it should probably be controlling its own open and closed states.
class App extends React.Component {
state= {
isOpen: false
}
handleClick = () => {
this.setState({
isOpen: !this.state.isOpen
})
}
render() {
return (
<div>
<YourComponent isOpen={this.state.isOpen} handleClick={this.handleClick} />
</div>
)
}
}
const YourComponent = ({isOpen, handleClick} = props) => {
const onClick = () => {
if (handleClick) {
handleClick();
}
}
return (
<div onClick={onClick}>
{isOpen ?
<h2>it is open</h2>
:
<h2>it is closed</h2>
}
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
If your major concern is about passing properties/methods down to too many components, you could create a pure component which will give you access to state but not all the overhead of a React Component subclass. You could also look into using a state management tool like Redux.

Calling a method that belongs to a child component from a component that sits in a parent component

I am trying to call a method in a child component from a component that sits on the paerent component. Please see below:
App class
// App.js
class App extends Component {
render() {
return (
<div className="App">
<SimpleModal/>
<FloatingButton onClick={/*would like to call HandleOpen in SimpleModal*/}/>
</div>
);
}
}
export default App;
And here's that SimpleModal component
//SimpleModal
class SimpleModal extends React.Component {
state = {
open: false,
};
handleOpen = () => {
this.setState({ open: true });
};
handleClose = () => {
this.setState({ open: false });
};
render() {
return (
<Modal
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
open={this.state.open}
onClose={this.handleClose}
>
<div style={getModalStyle()}>
<Typography type="title" id="modal-title">
Text in a modal
</Typography>
<Typography type="subheading" id="simple-modal-description">
Description
</Typography>
</div>
</Modal>
);
}
}
export default SimpleModal;
Basically I would like to call HandleOpen and HandleCall from FloatingButton. How should I do this? Thank you in advance.
Define both functions in the parent component (<App />) and pass them to the child via props. This way both <FloatingButton> and <SimpleModal> can have access to the same function. This technique is called "lift state up" and there is a very good article about it.
Instead of calling the functions from a sibling component, you could lift the state to the common parent component.
This allows you to pass the event handler into the FloatingButton component, and the current state of the modal into the Modal component.
For more infos, check out the relevant section in the React docs:
Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor.

Categories

Resources