Display a new component with button click - javascript

Im making my first react ptoject. Im new in JS, HTML, CSS and even web app programing.
What i try to do, is to display some infomration on button click.
I have an API, that looks like this:
endpoint: https://localhost:44344/api/Projects
My Data from it:
[{"id":1,"name":"Mini Jira","description":"Description for first project in list","tasks":null},{"id":2,"name":"Farm","description":"Description for second one","tasks":null}]
And im fine with that, i can get it easily by axios in my react app.
Now i will show you my Project.js Component:
import React, { Component } from "react";
import { ListGroupItem, Button, ButtonToolbar } from "react-bootstrap";
import ProjectDetails from "./ProjectDetails";
class Project extends Component {
render() {
return (
<ButtonToolbar>
<ListGroupItem>{this.props.project.name}</ListGroupItem>
<Button onClick={Here i want to display new component with details }bsStyle="primary">Details</Button>
</ButtonToolbar>
);
}
}
export default Project;
I have all data from api in project type.
My question is, how to display component that i named ProjectDetails.js on button click? I want to show all data stored in project from my api in separate view (new page or somethig like that).
View looks like this:
Thanks for any advices!
EDIT:
based on #Axnyff answer, i edited Project.js. it works ok. But when i want to (for testing) displat project.name, i get error map of undefined. My ProjectDetails.js:
import React, { Component } from "react";
class ProjectDetails extends Component {
state = {};
render() {
return <li>{this.props.project.name}</li>;
}
}
export default ProjectDetails;
EDIT2:
In Project.js in #Axnyff answet i just edited that line:
{this.state.showDetails && (
<ProjectDetails project={this.props.project} />
)}
i passed project by props, now it works like i want too. After click it displays project.name that i clicked on.

You should use state in your React component.
Let's create a field called showDetails in your state.
You can initialize it in your constructor with
constructor(props) {
super(props); // needed in javascript constructors
this.state = {
showDetails: false,
};
}
Then you need to modify the onClick to set that state to true
<Button onClick={() => this.setState({ showDetails : true })} bsStyle="primary">Details</Button>
And then use that state to show or not the ProjectDetails:
{ showDetails && <ProjectDetails /> }
The full component should look like
import React, { Component } from "react";
import { ListGroupItem, Button, ButtonToolbar } from "react-bootstrap";
import ProjectDetails from "./ProjectDetails";
class Project extends Component {
constructor(props) {
super(props); // needed in javascript constructors
this.state = {
showDetails: false,
};
}
render() {
return (
<ButtonToolbar>
<ListGroupItem>{this.props.project.name}</ListGroupItem>
<Button onClick={() => this.setState({ showDetails : true })} bsStyle="primary">Details</Button>
{ this.state.showDetails && <ProjectDetails /> }
</ButtonToolbar>
);
}
}
export default Project;
You can then modify the logic to add a toggling effect etc.
If you haven't done it, you should probably follow the official tutorial

function Bar() {
return <h1>I will be shown on click!</h1>;
}
class Foo extends React.Component {
constructor() {
super();
this.state = { showComponent: false };
}
handleClick = () => {
this.setState({ showComponent: !this.state.showComponent });
};
render() {
return (
<div>
{this.state.showComponent && <Bar />}
<button onClick={this.handleClick}>click</button>
</div>
);
}
}
ReactDOM.render(<Foo />, document.getElementById("root"));
<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>
<div id="root"></div>

Related

How to pass component to onClick in react

import React from 'react'
export default () => {
function clickHandler() {
console.log('Button clicked')
}
return (
<div>
<button onClick={clickHandler}>Click</button>
</div>
)
}
In the above code we see that a function has been passed to the onClick.In the same way to the onClick I need to pass a diffrent component which is present in the same src. This component consists of a .js and a .css file.Could you please help me out with it. Thanks in advance
If you don't mind using classes instead of functions, your other component should look like this:
import React from 'react'
class ShowThisAfterClick extends React.Component {
return (
<div>
<p>This is what you want to show</p>
</div>
)
}
export default ShowThisAfterClick
And now you should update the component you've shown:
import React from 'react'
import ShowThisAfterClick from './where/you/put/the/ShowThisAfterClick.js'
class Main extends React.Component {
constructor(props){
super(props)
this.state = { isButtonClicked: false }
this.clickHandler = this.clickhandler.bind(this)
}
clickHandler() {
this.setState({ isButtonClicked: true })
}
render() {
const { isButtonClicked } = this.state
return (
<div>
<button onClick={ this.clickHandler }>Click</button>
{ isButtonClicked ? <ShowThisAfterClick /> : ''}
</div>
)
}
}
export default Main
If you want to keep using functions, then I would kindly suggest to read the manual, it is more than well written.

how to pass value from one component to another in react js

I have one component in which I have one button and I am calling one node js service on that. I am getting a response back from that service and I want to pass that response on next component to display a data there. Below is my component which is doing a node js call.
import { FormGroup } from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.css";
import axios from "axios";
export default class Abc extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {}
}
this.handleClick = this.handleClick.bind(this);
}
handleClick = (e) => {
e.preventDefault();
axios.get(url)
.then(res => {
this.setState({
data: res.data// I need this variable to pass to next component Pqr where I can use it for display purpose.
})
this.props.history.push("/Pqr",{ response:res.data});
})
};
render() {
return (
<form >
<button className="btn btn-info btn-sm" onClick={this.handleClick} style={{ whitespace: 'nowrap' }} >
Launch
</button>
</form>
)
}
}
My Pqr component code is as below.
import React from "react";
export default class ModelLaunch extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
const state = this.props.location.state
return (
<h1>This page will display model Inputs : {state} </h1>
)
}
}
I have solved above problem with other way. Instead of calling a node js service on Abc component I am just redirecting it to new coponent and in new component's componentDidMount() method I am calling a node js service and storind a data in props. In this way I have my data on new copmonent. Below is my updated code in Abc component now.
import { FormGroup } from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.css";
import axios from "axios";
export default class Abc extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {}
}
this.handleClick = this.handleClick.bind(this);
}
handleClick = (e) => {
e.preventDefault();
this.props.history.push("/Pqr");
})
};
render() {
return (
<form >
<button className="btn btn-info btn-sm" onClick={this.handleClick} style={{ whitespace: 'nowrap' }} >
Launch
</button>
</form>
)
}
And in pqr coponent's code as below
import React from "react";
import axios from "axios";
export default class Pqr extends React.Component{
constructor(props)
{
super(props);
this.state = {
data :[]
}
}
componentDidMount(){
axios.get(url).then((res) => {
console.log("res****",res.data)
this.setState({
data:res.data
})
}).catch((err) =>{
console.log("err", err)
})
}
render()
{
return(
<h1>This page will display data</h1>
)
}
}
I see you're changing a route (using react-router?).
Remember that this.setState is async and specific for your component, when you call this.props.history.push('/Pqr'), maybe the this.state.data is not updated yet.
To share this data through different routes in the same react project, I actually know that you can:
Store it on window.localStorage and then get on the next route here have a tutorial
Use react contexts to share data between components (if you're not reloading the page)
Send data through routes with react-router, as explained here
If its not the case, and you just want to pass the property down or above the hierarchy tree, in addition to the comments above, maybe it can help:
As you probably know, react projects are composed of components that are put all together to work in a specific way. In the example below, there are two components (father and child)
import React from 'react';
// We can say that this component is "below" Father
function Child(props) {
return (
<button>
Hey, I'm a button!
</button>
);
}
// We can say that this component is "above" Child
function Father(props) {
return (
<div>
<Child />
</div>
);
}
I couldn't find in the provided code/question, one child component, maybe you forgot to write it?
If the response is "yes", I'll create a fictional component called... FictionalComponent (I'm a Genius!), and pass the data on state as a property named... data.
In order to pass this property, if its the case, you just need to update your render method to look like this:
render() {
return (
<form >
<button
className="btn btn-info btn-sm"
onClick={this.handleClick}
style={{ whitespace: 'nowrap' }}
>
Launch
<FictionalComponent data={this.state.data} />
</button>
</form>
)
}
This way, when this.state.data changes, the FictionalComponent will be re-rendered with the new data value.
But, maybe you want the reverse operation and you need to pass the this.state.data to the component above your Abc, listed there when the button is pressed.
To achieve it you need to have a "Father" component to your Abc, the "Father" component must provide an onDataChanged callback in order to capture the event. This callback will receive the data and handle it.
In this case, I'll create another component to be the component above your Abc. I'll name it... AboveAbcComponent, perfect!
...
class AboveAbcComponent extends React.Component {
constructor(props) {
this.state = {
dataFromChild: null
};
this.onDataChanged = this.onDataChanged.bind(this);
}
onDataChanged(dataReceived) {
console.log("Yey! It works!");
this.setState({ dataFromChild: dataReceived });
}
render() {// Just the passed props changes here
...
<Abc
onDataChanged={this.onDataChanged}
/>
...
}
}
export default class Abc extends React.Component {
constructor(props) { ... } // No changes here
handleClick = (e) => {
e.preventDefault();
axios.get(url)
.then(res => {
this.setState({
data: res.data
});
this.props.onDataChanged(res.data);
this.props.history.push("/Pqr"); // I really didn't understand Why this push is here... but ok
})
};
render() { ... } // No changes here
}
Hope it helps, have fun!

How to properly render Component after this.setState in React

I have this React component
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: [],
};
}
componentDidMount() {
// get the resources from the Link props and save it into the state
this.setState({
resources: this.props.location.resources,
});
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
It gets the resources from the Link component, and that works fine. If I check out the state of the Component from the dev tools, the state looks right. And I thought with my logic this should work. So firstly, the state is empty, the component gets rendered, since the state is empty it doesn't render any components. Then, setState gets called, it gets all the resources and saves them into the state, and then the component would re-render, and it should work, but it doesn't. I'm getting a TypeError: Cannot read property 'map' of undefined error. What is the correct way to do this and how do I fix this?
Try this code:
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: this.props && this.props.location && this.props.location.resources?this.props.location.resources:[],
};
}
componentDidMount() {
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
Or use directly props
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{
this.props && this.props.location &&
this.props.location.resources
?this.props.location.resources.map(res => (
<div>test</div>
))
:null
}
</div>
);
}
}
Or use componentWillReceiveProps or getDerivedStateFromProps life cycle methods.
Check this.props.location.resources is array.
See more: https://hackernoon.com/replacing-componentwillreceiveprops-with-getderivedstatefromprops-c3956f7ce607
For first check is this.props.location.resources array, or if data type changes you can add checking, you can use lodash isArray or with js like this:
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: [],
};
}
componentDidMount() {
// get the resources from the Link props and save it into the state
Array.isArray(this.props.location.resources) {
this.setState({
resources: this.props.location.resources,
});
}
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
Or you can just use hooks like this:
import React, { useState, useEffect } from "react";
export default function ResourceForField({location}) {
const [ resources, setResources ] = useState([]);
useEffect(() => {
if (location && Array.isArray(location.resources)) {
setResources(location.resources)
}
}, [location]);
return (
<div>
{resources.map(res => (
<div>test</div>
))}
</div>
);
}
If the internal state of ResourceForField doesn't change and always equals to its prop, you shouldn't save the prop in the state. You can instead create a pure functional component.
Also note that there's nothing preventing you from initializing the state from the props in constructor method. i.e. you're not required to wait for the component to mount in order to access the props.
So, I'd write the following component for ResourceForField:
function ResourceForField({resources = []}) {
return (
<div>
{
resources.map(res => (<div>test</div>))
}
</div>
);
}

React-Chat-Widget props not forwarded

I am using the react-chat-widget and trying to call a function in the base class of my application from a custom component rendered by the renderCustomComponent function of the widget.
Here is the code for the base class:
import React, { Component } from 'react';
import { Widget, handleNewUserMessage, addResponseMessage, addUserMessage, renderCustomComponent } from 'react-chat-widget';
import 'react-chat-widget/lib/styles.css';
import Reply from './Reply.js';
class App extends Component {
handleNewUserMessage = (newMessage) => {
renderCustomComponent(Reply, this.correct);
}
correct = () => {
console.log("success");
}
render() {
return (
<div className="App">
<Background />
<Widget
handleNewUserMessage={this.handleNewUserMessage}
/>
</div>
);
}
}
export default App;
And here is the code for the custom component Reply:
import React, { Component } from 'react';
import { Widget, addResponseMessage, renderCustomComponent, addUserMessage } from 'react-chat-widget';
class Reply extends Component {
constructor(props) {
super(props);
}
sendQuickReply = (reply) => {
console.log(this.props); //returns empty object
//this.props.correct(); <-- should be called
};
render() {
return (
<div className="message">
<div key="x" className={"response"}onClick={this.sendQuickReply.bind(this, "xx")}>xx</div>
</div>)
}
}
export default Reply;
According to ReactJS call parent method this should work. However, when I print the this.props object it is empty, although the documentation of the renderCustomComponent method states that the second argument of the component to render are the props that the component needs (in this case the parent class function).
Where have I gone wrong?
The second parameter is considered as props, but it is expected to be an object. you would pass it like
handleNewUserMessage = (newMessage) => {
renderCustomComponent(Reply, {correct: this.correct});
}

ReactJS: Separating Components Best Practices

So, I have a react-bootstrap nav and I want to have one of the nav items open and close a bootstrap modal component.
I have this working with this:
import React, { Component, render } from 'react';
import { Navbar, Nav, NavItem, NavDropdown, MenuItem, Modal, Button } from 'react-bootstrap';
export default class NavigationBar extends Component {
constructor() {
super();
this.state = {
showModal: false
}
}
close() { this.setState({ showModal: false }); }
open() { this.setState({ showModal: true }); }
render() {
return (
<div>
<Navbar>
...entire navbar...
</Navbar>
<Modal show={this.state.showModal} onHide={() => this.close()}>
...entire modal... which would be nice to put if a different file
</Modal>
</div>
); } }
Ideally, I would like to put the modal in a different component file and import it in, but when I do, I'm lost on how to translate the navbar open and close.
What is the best practice for combining components while maintaining their state across files?
Thanks!
A good way to think about it is containers vs presentational components. Containers hold your state and most of your logic. Presentational components take in inputs (props) and render html (jsx) [and do little else].
So, you could make your own Modal component that takes in the methods to call on close and one on whether or not it's shown. It could even be a stateless component - if it's just props + jsx, no need for a full class structure:
import React, { PropTypes } from 'react';
const MyModal = ({ show, onHide}) => (
<Modal show={show} onHide={onHide}>
// ...entire modal...
</Modal>
);
// displayName and propTypes are always good to have
MyModal.displayName = 'MyModal';
MyModal.propTypes = {
show: PropTypes.bool.isRequired,
onHide: PropTypes.func.isRequired,
};
export default MyModal;
then to use it, you will need to make sure to bind your methods so they're called in the right context:
class NavigationBar extends Component {
constructor() {
super();
this.state = {
showModal: false
};
// this is the important binding
this.close = this.close.bind(this);
this.open = this.open.bind(this);
}
close() { this.setState({ showModal: false }); }
open() { this.setState({ showModal: true }); }
render() {
return (
<div>
<Navbar>
// ...entire navbar...
</Navbar>
<MyModal
show={this.state.showModal}
onHide={this.close}
>
// child content if needed (unless it's all defined in MyModal)
</Modal>
</div>
);
}
}
You can wrap your react-bootstrap Modal with your content into your own custom component like so:
import React from 'react';
import { Modal } from 'react-bootstrap';
const NewModal = ({show, onHide}) => {
<Modal show={show} onHide={onHide}>
Modal content in here
</Modal>
};
export default NewModal;
And then import that modal from your component file
import Modal from 'components/modal' // Import your new modal's default export

Categories

Resources