I am wondering how to change a display few elements from hide to visible in React. I have 4 section and for each section button. Start section has start button, About section has about button, Skills section has skills button and Contact section has contact button. How to make it when im clicking Start all others sections get instantly display: none and only Skills section is visible? By clicking About button, others get hide (none) and only About is visible? Etc to others sections.
I know that i have to make a handleonclick but idk how.
Should it work with state?
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Start extends Component {
render() {
return (
<div className='start'>
</div>
);
}
}
class About extends Component {
render() {
return (
<div className='about'>
</div>
);
}
}
class Skills extends Component {
render() {
return (
<div className='skills'>
</div>
);
}
}
class Contact extends Component {
render() {
return (
<div className='contact'>
</div>
);
}
}
class Buttons extends Component {
render() {
return (
<div className="buttons">
<button>Start</button>
<button>About</button>
<button>Skills</button>
<button>Contact</button>
</div>
);
}
}
class App extends Component {
render() {
return (
<div className="App">
<Buttons />
<Main />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
<div id="root"></div>
<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>
Keep a state activeSection in the parent container called App. Then, pass it as a props to the child section components, About, Skills etc. Also add a method handleToggleSection, which you can call on click to the buttons and change the state activeSection to that corresponding section name. Inside all section components, About, Skills etc., check the current section name. If the name matches, then return the html or return null. Remember, when you return null, that component don't mount. If you want to keep the components mount regardless they are visible or not, then u need to use css classes like show, hide etc.
Here is the demo.
// import React from "react";
// import ReactDOM from "react-dom";
class Start extends React.Component {
get show() {
return this.props.activeSection === "start";
}
render() {
if (this.show) {
return <div className="start"> Start </div>;
} else {
return null;
}
}
}
class About extends React.Component {
get show() {
return this.props.activeSection === "about";
}
render() {
if (this.show) {
return <div className="about"> About </div>;
} else {
return null;
}
}
}
class Skills extends React.Component {
get show() {
return this.props.activeSection === "skills";
}
render() {
if (this.show) {
return <div className="skills"> Skills </div>;
} else {
return null;
}
}
}
class Contact extends React.Component {
get show() {
return this.props.activeSection === "contact";
}
render() {
if (this.show) {
return <div className="contact"> Contact </div>;
} else {
return null;
}
}
}
const Buttons = ({ onToggle }) => (
<div className="buttons">
<button name="start" onClick={onToggle}>
Start
</button>
<button name="about" onClick={onToggle}>
About
</button>
<button name="skills" onClick={onToggle}>
Skills
</button>
<button name="contact" onClick={onToggle}>
Contact
</button>
</div>
);
const Main = ({ activeSection }) => (
<React.Fragment>
<Start activeSection={activeSection} />
<About activeSection={activeSection} />
<Skills activeSection={activeSection} />
<Contact activeSection={activeSection} />
</React.Fragment>
);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activeSection: ""
};
this.handleToggleSection = this.handleToggleSection.bind(this);
}
handleToggleSection(e) {
const { name } = e.target;
this.setState(() => ({
activeSection: name
}));
}
render() {
return (
<div className="App">
<Buttons onToggle={this.handleToggleSection} />
<Main activeSection={this.state.activeSection} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
.App {
font-family: sans-serif;
text-align: center;
}
.show {
display: block;
}
.hide {
display: none;
}
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can use conditional rendering.
selectSection = section => {
this.setState({ section })
}
render() {
return (
<div className="App">
<Buttons onClick={this.selectSection} />
{this.state.section === "start" && <Start>}
{this.state.section === "about" && <About>}
</div>
);
}
Also, instead of the if you can use switch.
Related
class UserForm extends React.Component {
constructor(props) {
super(props);
const { user } = props;
}
_cancelForm() {
this.props.onCancel();
}
render() {
return (
<button onClick={this._cancelForm.bind(this)}> Cancel </button>
);
}
}
class UserCreate extends React.Component {
_navigateToLogin() {
console.log("hi")
}
render() {
return (
<div>
<UserForm onCancel={this._navigateToLogin.bind(this)}/>
</div>
);
}
}
ReactDOM.render(
<UserCreate/>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
import React from 'react'
import { withRouter } from 'react-router-dom';
import UserForm from './UserForm'
import UsersService from '../services/UsersService'
class UserCreate extends React.Component{
_navigateToLogin() {
this.props.history.push('/homepage');
}
async _saveUser(user) {
await UsersService.createUser(user);
this._navigateToLogin();
}
render() {
return(
<div>
<UserForm
onCancel={this._navigateToLogin.bind(this)}
onSubmit={this._saveUser.bind(this)}
/>
</div>
);
}
}
export default withRouter(UserCreate)
import React from 'react'
import {
Button
} from '#material-ui/core'
export default class UserForm extends React.Component {
constructor(props) {
super(props);
const { user } = props;
this.state = {
...
}
_handleFormSubmit() {
const user = {
...
};
this.props.onSubmit(user);
}
_cancelForm() {
this.props.onCancel();
}
render () {
return (
<div style={{ width: '100%', height: 'auto', position: 'fixed', minWidth: '100%', minHeight: '100%', backgroundColor: '#50617C' }}>
<Button size="small" onClick={ this._cancelForm.bind(this) }>Back</Button>
<Button size="small" onClick={ this._handleFormSubmit.bind(this) }>Create</Button>
</div>
)
}
}
The error is present when I click the "Back" button on CreateAccountForm, it returns an error that says that the onCancel function is not a function. I'm sending it on the UserCreate by binding and I'm calling it on the function _cancelForm(). I was thinking that the error is that I'm missing something in the constructor according to some react documentation, I used before this method and it worked, right now I don't know what's happening.
The code you posted does not demonstrate the error, below is your code and it works just fine.
class UserForm extends React.Component {
constructor(props) {
super(props);
}
_cancelForm() {
this.props.onCancel();
}
render() {
return (
<button onClick={this._cancelForm.bind(this)}>
cancel
</button>
);
}
}
class UserCreate extends React.Component {
_navigateToLogin() {
console.log('in navigate login');
}
render() {
return (
<div>
<UserForm
onCancel={this._navigateToLogin.bind(this)}
/>
</div>
);
}
}
ReactDOM.render(
<UserCreate />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Can you provide a minimal snippet that reproduces the error you are getting?
You also don't need to bind the handler if you use arrow functions. For example:
_navigateToLogin = () => {//arrow function is automatically bound to this
console.log('in navigate login');
};
and
<UserForm onCancel={this._navigateToLogin} />
import './App.css';
import SolarSystem from './components/solarSystem/solarSystem';
class App extends React.Component {
componentDidMount(){
console.log("mounting");
}
componentDidUpdate(){
console.log("updating");
}
//const [SSVisibility, setSSVisibility] = useState(true);
render(){
console.log("rendering app");
return (
<div className="App">ssssssssssssssssssssssssssssssss
{/* <SolarSystem isShowing={"yolo"} toggle={"polo"}></SolarSystem> */}
</div>
);
}
}
export default App;
With this simple code, my render method is being called twice. And i cant understand why
It is because of strict mode, code below doesn't demonstrate it because SO will build it with production set true (I think).
class Strict extends React.Component {
componentDidMount() {
console.log('mounting strict');
}
componentDidUpdate() {
console.log('updating');
}
//const [SSVisibility, setSSVisibility] = useState(true);
render() {
console.log('rendering strict');
return (
<div className="App">
{/* <SolarSystem isShowing={"yolo"} toggle={"polo"}></SolarSystem> */}
</div>
);
}
}
class NonStrict extends React.Component {
componentDidMount() {
console.log('mounting non strict');
}
componentDidUpdate() {
console.log('updating');
}
//const [SSVisibility, setSSVisibility] = useState(true);
render() {
console.log('rendering Non strict');
return (
<div className="App">
{/* <SolarSystem isShowing={"yolo"} toggle={"polo"}></SolarSystem> */}
</div>
);
}
}
const App = () => {
return (
<div>
<React.StrictMode>
<Strict />
</React.StrictMode>
<NonStrict />
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
what I am trying to achieve here is being able to move to Page1 component from App component by clicking a button.
The result I am getting is
Page 1
Let's get started
Go!
The result I want to achieve is
Page 1
Please point out what I am doing wrong here. Thank you.
import React, { Component } from 'react';
import Page1 from './comps/Page1';
class App extends Component {
state = {
page:0
}
HandlePage = () => {
this.setState({
page:1
})
}
render() {
let comp = null;
if(this.state.page === 1){
comp = <Page1/>
}
return (
<div className="App">
{comp}
<h1>Let's get started!!</h1>
<div className="button">
<button
type="submit"
onClick={this.HandlePage}
>GO</button>
</div>
</div>
);
}
}
export default App;
//Page 1 Component
import React, { Component } from 'react';
class Page1 extends Component {
render() {
return (
<div className="App">
<h1>Page 1</h1>
</div>
);
}
}
export default Page1;
Here is a snippet of how you can achieve it by conditional rendering:
const Component = React.Component;
class App extends Component {
state = {
page:0
}
handlePage = () => {
this.setState({
page:1
})
}
render() {
let Comp = null;
if(this.state.page === 1){
Comp = <Page1/>
} else if(this.state.page === 0) {
Comp = <StartPage handlePage={this.handlePage} />
}
return (
<div className="App">
{Comp}
</div>
);
}
}
function StartPage(props) {
return (
<div>
<h1>Let's get started!!</h1>
<div className="button">
<button
type="submit"
onClick={props.handlePage}
>GO</button>
</div>
</div>
);
}
class Page1 extends Component {
render() {
return (
<div className="App">
<h1>Page 1</h1>
</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 you have more pages to display, I would encourage you to look at solution like React Router
I'm sorry if this is a frequent question but it's something I'm reallly struggling to understand.
I'm building a basic to-do list app in React.
The container currently contains a form that takes in the information, and adds each item inputted an items array in the state. I then have a 'TaskList' component that takes this state and renders my tasks.
What I want to do is create a separate form component, instead of having the form within my container.
The issue is that if I just copy the code for the form into a new component, the state it will modify is its own, and therefore won't be accessible via the TaskList component to render the list of tasks.
Is there any way to have a component that can update the state of its parent component. My source code is below for reference.
export default class Container extends React.Component {
constructor() {
super();
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
items: []
}
}
handleSubmit(e) {
e.preventDefault();
var itemsArray = this.state.items;
itemsArray.push(e.target.elements.task.value);
this.setState({
items: itemsArray
})
e.target.reset();
}
render() {
return (
<div className="">
<header className="header">TODO</header>
<form onSubmit={this.handleSubmit}>
<input name="task" placeholder="Task"></input>
<button type="submit">Add</button>
</form>
<TaskList data={this.state.items} />
</div>
);
}
}
<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>
const TaskList = props => {
var tasks = (props.data).map( (item, key) => { return <Task data={item} key={key} /> })
return(
<ul className="gif-list">
{tasks}
</ul>
);
}
export default TaskList;
<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>
You can do it this way. Pass the parent function that change the state as a props to the form component. In the form handle submit function, call the parent function as this.props.addTodo(todoText).
Container.js
import React, { Component } from 'react';
import TaskList from './TaskList';
import Form from './Form';
export default class Container extends Component {
constructor() {
super();
this.handleAdd = this.handleAdd.bind(this);
this.state = {
items: []
}
}
handleAdd(todoText) {
var itemsArray = this.state.items;
itemsArray.push(todoText);
this.setState({
items: itemsArray
})
}
render() {
return (
<div className="">
<header className="header">TODO</header>
<Form addTodo={this.handleAdd}/>
<TaskList data={this.state.items} />
</div>
);
}
}
Form.js
import React, { Component } from 'react';
export default class Form extends Component {
handleSubmit(e) {
e.preventDefault();
let todoText = e.target.elements.task.value;
if(todoText.length > 0) {
e.target.elements.task.value = '';
this.props.addTodo(todoText);
}else{
e.target.elements.task.focus();
}
}
render() {
return(
<div>
<form onSubmit={(e) => this.handleSubmit(e)}>
<input name="task" placeholder="Task"></input>
<button type="submit">Add</button>
</form>
</div>
);
}
}
TaskList.js
import React from 'react';
const TaskList = props => {
var tasks = (props.data).map( (item, key) => { return <li key={key}>{item}</li> })
return(
<ul className="gif-list">
{tasks}
</ul>
);
}
export default TaskList;
I have the following spinner
import React, { Component } from 'react'
import './Spinner.scss'
export default class Spinner extends Component {
constructor(props) {
super(props);
this.state = {showLoading: true};
}
render () {
return (
<div className="spinner">
<div className="double-bounce1"></div>
<div className="double-bounce2"></div>
</div>
)
}
}
and from other component I would like to show or hide this spinner here is the code of the component:
import React, { Component } from 'react'
import RTable from '../../../components/RTable/RTable'
import Spinner from '../../../components/Spinner/Spinner'
import CsvDownload from '../containers/CsvDownloadContainer'
export default class Table extends Component {
_renderBreadcrumb () {
const { breadcrumb, handleBreadcrumbClick } = this.props
return (
<ol className="breadcrumb">
{(breadcrumb || []).map(el => {
return (
<li key={el.datasetKey}>
<a onClick={() => { handleBreadcrumbClick(el.granularity, el.datasetKey, el.datasetKeyHuman) }}>
{el.datasetKeyHuman}
</a>
</li>
)
})}
</ol>
)
}
render () {
const { datasetRows, columns, metadata, showLoading } = this.props
return (
<div className="row">
<div className="col-sm-12">
{this._renderBreadcrumb()}
<RTable rows={datasetRows} columns={columns} metadata={metadata} />
{ this.props.showLoading ? <Spinner /> : null }
<CsvDownload />
</div>
</div>
)
}
}
as you can see I trying to show or hide the spinner using:
{ this.props.showLoading ? <Spinner /> : null }
but I'm always getting undefinde. Some help please.
You have to move this
constructor(props) {
super(props);
this.state = {showLoading: true};
}
to your <Table /> component, otherwise you access showLoading from <Table />'s props, but it is not passed from anywhere.
Then change also
{ this.props.showLoading ? <Spinner /> : null }
to
{ this.state.showLoading ? <Spinner /> : null }
To show / hide <Spinner /> just call this.setState({ showLoading: Boolean }) in your <Table /> component.