I have a react component which I want to self close when I click a button that's on the component itself.
Here's the code:
import React from 'react';
import PropTypes from 'prop-types';
const MyReactComponent = (props) => <div>
<h1>TEST</h1>
<button onClick={self close here?}>Self Close</button>
</div>
export default MyReactComponent
How can I get the button click to close the component when I click it?
That's not how React works. :-) Instead, the parent of the component should pass it a property that it uses as the onClick. In response to the click, the parent component changes its state so that the child is no longer rendered:
const MyReactComponent = (props) => <div>
<h1>TEST</h1>
<button onClick={props.onClose}>Self Close</button>
</div>;
class ParentComponent extends React.Component {
// Note: This uses the class fields proposal, currently at Stage 3 and
// commonly transpiled in React projects
closeChild = () => {
this.setState({
showChild: false
});
};
constructor(...args) {
super(...args);
this.state = {
showChild: true
};
}
render() {
return (
<div>
{this.state.showChild && <MyReactComponent onClose={this.closeChild} />}
</div>
);
}
}
ReactDOM.render(
<ParentComponent />,
document.getElementById("root")
);
<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>
More in the "Lifting State Up" part of the documentation.
Related
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
Hello I'm trying to change the state when I click the button and only when the state changes run createBattle() but the state does not change after I click the button.
At the beginning I set the state to false. The button is in Form.js with an event onClick={this.handleClick}. Then the event handleClick should set the state to true and when the state changes createBattle() in Battle.js should render the table.
Please tell me what am I doing wrong ?
Thanks
App.js
import React from "react";
import Titles from "./Components/Title";
import Form from "./Components/Form";
import Battle from "./Components/Battle";
import "./App.css";
class App extends React.Component{
state = {
startPosition : false
}
render(){
return(
<div>
<header>
<div className="meniu"></div>
</header>
<div className="wrapper">
<div className="main">
<div className="container">
<div className="title-container">
<Titles />
<div className="info">
<Form startPosition={this.state.startPosition} />
</div>
</div>
<div className="form-container">
<Battle startPosition={this.state.startPosition}/>
</div>
</div>
</div>
</div>
</div>
);
}
};
export default App;
Battle.js
import React, {Component} from "react";
import Square from "./Square";
class Battle extends Component{
constructor(){
super();
}
createBattle = () => {
let table=[];
for (let i=1; i<=10; i++){
let children = [];
for (let j=1; j<=10; j++){
children.push(<Square />)
}
table.push(<div className="board-row">{children}</div>)
}
return table;
}
render(){
console.log(this.props);
return(
<div className="center">
{this.startPosition && this.props.createBattle()}
</div>
);
}
}
export default Battle;
Form.js
import React from "react";
class Form extends React.Component{
constructor(){
super();
}
handleClick = () =>{
this.setState({
startPosition: true
});
};
render(){
console.log(this.props);
return(
<div>
<button className="button" onClick={this.handleClick}>START</button>
</div>
);
}
};
export default Form;
The state and props of a given component is not shared across other components.
If you need to communicate between components you mostly have 2 different options :
Move state logic to a common parent and pass this state as props in child components (Note that you may also need to pass some functions to allow to interact with this parent state from the child components)
Use a common state, with a framework like Redux (widely used in complex projects)
React does not support sharing of state or props values.
So you should use any of the following
React Redux
AsyncStorage
I've created an application to React, and when it starts, the App component is rendered. I would like that when the user clicks on a button or link, the button or link has to be in the App component when clicking on that link, another component will be rendered but not inside the App component but only the new component will be rendered in the same URL. As for this new component, it has to have a similar button so that when the user clicks, only the App component is rendered and this component that the user has clicked on is not rendered, only the App component.
I do not know if I explained myself correctly. Ask me any question if you need some clarification.
My App component is the following:
import React, { Component } from 'react';
import Touch from './Touch';
import '../App.css';
class App extends Component{
render() {
return(
<div>
<div className="wrapper" >
<button >NewComponent</button><NewComponent />???
<h1>Google Cloud Speech with Socket.io</h1>
<p id="ResultText"><span className="greyText">No Speech to Text yet</span></p>
</div>
<div className="buttonWrapper" >
<button className="btn" id="startRecButton" type="button"> Start recording</button>
<button className="btn" id="stopRecButton" type="button"> Stop recording</button>
</div>
</div>
);
}
}
export default App
My index.js is the following:
import React from 'react';
import ReactDOM from 'react-dom';
import './App.css';
import App from './components/App.js';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
If you really don't want to use react-router you will need to store a value in the component's state and change the rendering method to reflect which button was pressed. If you want each of those component to include the button you need to switch, do the following :
class App extends Component {
constructor(props){
super(props);
this.state = {renderA: false,};
}
handleClick = (event) => {
this.setState((prevState) => ({renderA: !prevState.renderA}));
};
render = () => {
return(
<div>
{this.state.renderA ?
<ComponentA handleClick={this.handleCLick}/>:
<ComponentB handleClick={this.handleCLick}/>
}
</div>
);
};
} export default App;
// ComponentA
class ComponentA extends Component {
render = () => {
return(
<div>
// what you want inside your first page here
<button onClick={this.props.handleClick}
</div>
);
}
} export default ComponentA;
// ComponentB
class ComponentB extends Component {
render = () => {
return(
<div>
// what you want inside your second page here
<button onClick={this.props.handleClick}
</div>
);
}
} export default ComponentB;
But using react-router might also suits your case, and if you are going to write a large app, you should use it instead of rendering differents children components within the same one, based on users inputs.
If the URL stay the same, I don't think React-Router might help you.
If you want that App Component is not loaded, I think you should create two more Component, a Wrapper one, and the new component you want to display (from now on newComponent). What I suggest is:
Creating a property isButtonClicked inside the state of the Wrapper Component;
Creating a function handleButtonClick() inside the Wrapper Component:
handleButtonClick() => {
let isButtonClicked = !this.state.isButtonClicked;
this.setState({ isButtonClicked });
}
In the render() method of the Wrapper component, you write something like this:
render() {
if (this.state.isButtonClicked)
return <App />
else
return <NewComponent />
}
Then, in both App and NewComponent, if you click on the button, you call the this.props.handleButtonClick(), which will lead to a change of the state of Wrapper Component, therefore to a change of what is shown on the screen.
I am trying to render a paragraph as soon as the you click the button.
Here is my code.
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.createText = this.createText.bind(this);
}
createText() {
return(
<p>hello friend</p>
)
}
render() {
return (
<div className="App">
<button onClick={this.createText}>Click</button>
</div>
);
}
}
export default App;
Here I am trying to render "hello friend" when the button is clicked. But is not working.
This is not the correct way because createText is a event handler it will not render the element, what you need is "Conditional rendering of elements".
Check Docs for more details Conditional Rendering.
Steps:
1- Use a state variable with initial value false.
2- Onclick of button update the value to true.
3- Use that state value for conditional rendering.
Working Code:
class App extends React.Component {
constructor() {
super();
this.state = {
isShow: false
}
this.createText = this.createText.bind(this);
}
createText() {
this.setState({ isShow: true })
}
render() {
return (
<div className="App">
<button onClick={this.createText}>Click</button>
{this.state.isShow && <p>Hello World!!!</p>}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'))
<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='app' />
I am trying to render a paragraph as soon as the you click the button.
Here is my code.
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.createText = this.createText.bind(this);
}
createText() {
return(
<p>hello friend</p>
)
}
render() {
return (
<div className="App">
<button onClick={this.createText}>Click</button>
</div>
);
}
}
export default App;
Here I am trying to render "hello friend" when the button is clicked. But is not working.
This is not the correct way because createText is a event handler it will not render the element, what you need is "Conditional rendering of elements".
Check Docs for more details Conditional Rendering.
Steps:
1- Use a state variable with initial value false.
2- Onclick of button update the value to true.
3- Use that state value for conditional rendering.
Working Code:
class App extends React.Component {
constructor() {
super();
this.state = {
isShow: false
}
this.createText = this.createText.bind(this);
}
createText() {
this.setState({ isShow: true })
}
render() {
return (
<div className="App">
<button onClick={this.createText}>Click</button>
{this.state.isShow && <p>Hello World!!!</p>}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'))
<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='app' />