how to call functional component in class based component using onClick event? - javascript

i want to show my functional component in class base component but it is not working. i made simpletable component which is function based and it is showing only table with some values but i want to show it when i clicked on Show user button.
import React ,{Component} from 'react';
import Button from '#material-ui/core/Button';
import SimpleTable from "../userList/result/result";
class ShowUser extends Component{
constructor(props) {
super(props);
this.userList = this.userList.bind(this);
}
userList = () => {
//console.log('You just clicked a recipe name.');
<SimpleTable/>
}
render() {
return (
<div>
<Button variant="contained" color="primary" onClick={this.userList} >
Show User List
</Button>
</div>
);
}
}
export default ShowUser;

Why your code is not working
SimpleTable has to be rendered, so you need to place it inside the render method. Anything that needs to be rendered inside your component has to be placed there
On Click can just contain SimpleTable, it should be used to change the value of the state variable that controls if or not your component will be shown. How do you expect this to work, you are not rendering the table.
Below is how your code should look like to accomplish what you want :
import React ,{Component} from 'react';
import Button from '#material-ui/core/Button';
import SimpleTable from "../userList/result/result";
class ShowUser extends Component{
constructor(props) {
super(props);
this.state = { showUserList : false }
this.userList = this.userList.bind(this);
}
showUserList = () => {
this.setState({ showUserList : true });
}
render() {
return (
<div>
<Button variant="contained" color="primary" onClick={this.showUserList} >
Show User List
</Button>
{this.state.showUserList ? <SimpleTable/> : null}
</div>
);
}
}
export default ShowUser;
You can also add a hideUserList method for some other click.
Or even better a toggleUserList
this.setState({ showUserList : !this.state.showUserList});

If you're referring to the method userList then it appears that you're assuming there is an implicit return value. Because you're using curly braces you need to explicitly return from the function meaning:
const explicitReturn = () => { 134 };
explicitReturn(); <-- returns undefined
const implicitReturn = () => (134);
implicitReturn(); <-- returns 134

The problem lies with how you are trying to display the SimpleTable component. You are using it inside the userList function, but this is incorrect. Only use React elements inside the render method.
What you can do instead is use a state, to toggle the display of the component. Like this:
const SimpleTable = () => (
<p>SimpleTable</p>
);
class ShowUser extends React.Component {
constructor(props) {
super(props);
this.state = {showSimpleTable: false};
this.toggle= this.toggle.bind(this);
}
toggle = () => {
this.setState(prev => ({showSimpleTable: !prev.showSimpleTable}));
}
render() {
return (
<div>
<button variant = "contained" color = "primary" onClick={this.toggle}>
Show User List
</button>
{this.state.showSimpleTable && <SimpleTable />}
</div>
);
}
}
ReactDOM.render(<ShowUser />, document.getElementById("app"));
<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="app"></div>

The functionality you are looking for is called Conditional Rendering. The onClick prop function is an event handler and events in react may be used to change the state of a component. That state then may be used to render the components. In normal vanilla javascript or jQuery we call a function and modify the actual DOM to manipulate the UI. But React works with a virtual DOM. You can achieve the functionality you are looking for as follows:
class ShowUser extends Component {
constructor(props) {
super(props)
// This state will control whether the simple table renders or not
this.state = {
showTable: false
}
this.userList.bind(this)
}
// Now when this function is called it will set the state showTable to true
// Setting the state in react re-renders the component (calls the render method again)
userList() {
this.setState({ showTable: true })
}
render() {
const { showTable } = this.state
return (
<div>
<Button variant="contained" color="primary" onClick={this.userList}>
Show User List
</Button>
{/* if showTable is true then <SimpleTable /> is rendered if falls nothing is rendered */}
{showTable && <SimpleTable />}
</div>
)
}
}

Related

Can't Get React Component to Update or Pass Prop

All the code does is to update a counter +1 every time you click in the button, the problem here is when i'm trying to pass the prop counter to text it does not update in the Text component i've been doing some research and look like i have to wake it up with another function, if someone could explain me, i would be really grateful.
import React from 'react';
import ReactDOM from 'react-dom';
class Button extends React.Component {
constructor(props) {
super(props)
this.state = {counter: 1}
}
render() {
return (
<button onClick={() => {
this.setState({
counter: this.state.counter+1
});
//tries passing the prop counter with the state of the counter
<Text counter={this.state.counter} />
}}>
Click here
</button>
)
}
}
class Text extends React.Component {
render() {
return (
// returns 'clicked undefined times'
<h2 id='counter-text'>{'Clicked ' + this.props.counter + ' times'}</h2>
)
}
}
class App extends React.Component {
render() {
return (
<>
<Text/>
<Button/>
</>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'));
Well, the code is syntactically wrong.
You cannot render a Text in the onChange method of a button.
What you wanted was, when the count is updated in the Button, it should reflect in another component, i.e., the Text.
As these two are different components altogether, you have to have a shared state for them.
And for that, you can lift the counter state from Button to a parent component App. Check this out: https://reactjs.org/docs/lifting-state-up.html
This should work:
import React from "react";
import ReactDOM from "react-dom";
class Button extends React.Component {
render() {
// 3. on every click, Button uses App's updater method to update the count
return <button onClick={this.props.handleCounter}>Click here</button>;
}
}
class Text extends React.Component {
render() {
return (
<h2 id="counter-text">{"Clicked " + this.props.counter + " times"}</h2>
);
}
}
// 1. Let App bear the counter state
class App extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 1 };
this.handleCounter = this.handleCounter.bind(this);
}
handleCounter() {
this.setState({
counter: this.state.counter + 1
});
}
// 2. Let the Button and Text components share the App state
render() {
return (
<>
<Text counter={this.state.counter} />
<Button
counter={this.state.counter}
handleCounter={this.handleCounter}
/>
</>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
There's a few mistakes on how your code is written.
I've changed it slightly and put it on a sandbox you can play around with.
What's wrong:
You're trying to render <Text counter={this.state.counter} /> from
within the onClick callback for the button, but this won't ever
render without being inside the return statement of a render
function.
You're using state inside the Button component, but Text component is not a child of this component.
Both Button and Text are children of App, and both of them need
to access this counter state, so App should have the state and pass
it as prop for Text
Button needs a callback to be able to update it's parent (App) state. You can pass a function that does this as a prop to Button and then call this on onClick.

Switch child components by state

I have a parent component:
import React, { Component, Fragment } from "react";
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
SignUpClicked: null
};
this.openSignUp = this.openSignUp.bind(this)
}
openSignUp() {
this.setState({
SignUpClicked: true
})
console.log('signup clicked')
}
render() {
return (
<div>
<SignUp
authState={this.props.authState}
onStateChange={this.props.onStateChange}
openSignUp = {this.openSignUp} />
<SignIn
authState={this.props.authState}
onStateChange={this.props.onStateChange} />
</div>)
}
}
export default Parent;
and then two child components SignUp & SignIn in different files.
In each of them there's a link like <p>Sign Up Instead?<a href="#" onClick = {this.props.openSignUp}> Sign Up </a></p> and the other way for Sign In.
However, I can't get them to switch between the two components - what am I doing wrong here?
You can easily control which component should be rendered by putting condition with them. If this.props.authState is true, show SignUp component else show SignIn component
<div>
{!this.props.authState && (<SignUp
authState={this.props.authState}
onStateChange={this.props.onStateChange}
openSignUp = {this.openSignUp} />) }
{this.props.authState && (<SignIn
authState={this.props.authState}
onStateChange={this.props.onStateChange} />)}
</div>
There is a concept called conditional rendering you can use that can solve your problem. Simply put in conditional rendering you display a component only when a condition is met. In your case, you can try the following.
import React, { Component, Fragment } from "react";
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
SignUpClicked: false
};
}
//changed this to arrow function that binds "this" automatically
toggleSignUp = () => {
this.setState({
SignUpClicked: !this.state.SignUpClicked
})
console.log('signup clicked')
}
render() {
return (
<div>
{ this.state.SignUpClicked &&
<SignUp
authState={this.props.authState}
onStateChange={this.props.onStateChange}
openSignIn = {this.toggleSignUp} />
}
{!this.state.SignUpClicked &&
<SignIn
authState={this.props.authState}
onStateChange={this.props.onStateChange}
openSignUp = {this.toggleSignUp} />
/>
}
</div>
)}
}
export default Parent;
NOTE: Pay attention to the changes I have done
Changed the function to arrow function by using them you don't have to bind this in constructor. It automatically does that for you.
I have changed the name of function openSignUp to toggleSignUp because we will use a single function to display signup component and than hide it if we want. (because I assume you will implement "sign in instead" in <SignUp/> component to get back to sign in
I have passed the same toggleSignUp function reference to both the components so that you can show or hide either of them.
Do it this way
import React, { Component, Fragment } from "react";
class Parent extends Component {
constructor(props) {
super(props);
// Set state of the component
this.state = {
// Show sign in page by default
show: "signin"
};
}
showSignup = () => {
// Show sign up page by changing the show variable
this.setState({
show: "signup"
});
console.log('Showing Signup Page');
}
showSignin = () => {
// Show sign in page by changing the show variable
this.setState({
show: "signin"
});
console.log('Showing Signin Page');
}
render() {
// Render the component as per show state variable
if(this.state.show === "signin") {
return <SignIn
authState={this.props.authState}
onStateChange={this.props.onStateChange}
onSignup={this.showSignup}
/>
}
else {
return <SignUp
authState={this.props.authState}
onSignup={this.showSignin}
/>
}
}
export default Parent;
So basically, export onClick event from both the child components and set show variable of state in parent component. Then depending upon the state, return only the component you want.
Please let me know if there is any question or confusion. Would love to answer.

Show/Hide a component in React using state

I'm trying to hide / show a component while checking a state value :
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Button from '#material-ui/core/Button'
class App extends Component {
constructor(props) {
super(props);
this.state = true;
}
render() {
return (
<div className="App">
{this.state && <Button variant="raised" onClick={this.state=false}>Button</Button>}
</div>
);
}
}
export default App;
I don't know where's the bug / wrong code, but the render doesn't seems to refresh.
You are using it incorrectly. You have to init the state like this
constructor(props) {
super(props);
this.state = {show: true}
}
and render it like this
return (
<div className="App">
{this.state.show && <Button variant="raised" onClick={() => this.setState({show:false})}>Button</Button>}
</div>
);
You can't declare this.state as a variable, that should return an error on compile. As Murat said.
State in react is a JS object and is declared as such:
this.state = { ... }
In order to mutate (change) the state, you can't do it directly in react or else react will not know it has changed. If you were to say declare your state as:
this.state = {show: true}
and then wanted to change the boolean to false, you couldn't do it by simply doing this.state.show = false. React wouldn't know that the change had happened, that is why you need to use a react method called setState(). As Murat said you will change the state by using the following onClick metod: onClick={() => this.setState({show:false})}.
You should check this documentation page about react state for more on it, it is an important part of working with React.JS.
https://reactjs.org/docs/state-and-lifecycle.html
There's a section on the article titled "Using state correctly" have a look at that. :)
In a a React component, this.state is expected to be an object representing the state of your component.
1) this.state must be initialized (either in the constructor or in the class body)
example in the constructor:
constructor(props) {
super(props);
this.state = {
showButton: true
};
}
2) The event handler must be a reference to a method (this.handleButtonClick) of your component and this method must be bound to the component instance:
constructor(props) {
super(props);
this.state = {
showButton: true
};
this.handleButtonClick = this.handleButtonClick.bind(this);
}
handleButtonClick(event) {
this.setState(() => ({ showButton: false }));
}
render() {
return (
<div className="App">
{this.state.showButton && <Button variant="raised" onClick={this.handleButtonClick}>Button</Button>}
</div>
);
}
Note that you musn't set state directly but rather use the setState method, the signature of setState that I used is the updater form, look into react documentation for explaination on this.

React/ ESLint - JSX props should not use arrow functions

I'm currently creating a component in react and i'm using the ES Lint rule react/jsx-no-bind. My issue here is that I want to be able to pass a parameter to my components function. Here is the code I would like to use to be able to do so:
class LanguageDropdown extends Component {
constructor(props) {
super(props);
this.state = {};
}
changeLanguage = (lang) => {
console.log(lang)
};
render() {
return (
<div>
{this.props.languages.map(lang => <button onCLick={() => this.changeLanguage(lang)}>{lang}</button>)}
</div>
)
}
...
This pulls up the ESlint error:
JSX props should not use arrow functions
I'm not entirely sure how to achieve this without using an arrow function or using .bind(). I could add a data-attribute to the button element and then just pass in the event into the changeLanguage function and fetch the attribute using event.target() but this doesn't feel like it's the way it should be approached in React.
Can someone tell me what would be the correct way?
You can refactor button into its own component:
class MyButton extends Component {
static propTypes = {
language: PropTypes.string.isRequired,
};
onClick = () => console.log(this.props.language);
render() {
const {language} = this.props;
return (
<button onClick={this.onClick} type="submit">
{language}
</button>);
}
}
and then in your LanguageDropDown class, use MyButton like this:
class LanguageDropdown extends Component {
...
render() {
return (
<div>
{this.props.languages.map(lang => <MyButton key={lang} language={lang}/>)}
</div>
)
}
...
}
A couple of additional things:
You have a typo onCLick should be onClick
You need a key for repeated items
try the below code.
here I tried by taking the value into the state, same can be tried using props.
class LanguageDropdown extends Component {
constructor(props) {
super(props);
this.state = {languages:['telugu','hindi','english']};
// this.changeLanguage = this.changeLanguage.bind(this);
}
changeLanguage(event,lang){
//event.preventDefault();
console.log('change lang: '+JSON.stringify(lang));
};
render() {
return (
<div>
{this.state.languages.map(lang => <button onClick={(event)=>this.changeLanguage(event,lang)}>{lang}</button>)}
</div>
)
}
}
render(<LanguageDropdown />, document.getElementById('root'));
when you bind the handler in the onClick event where you are passing the value to the handler, then we have to pass that value from the event and collect it to get that value.

React - How to pass data among parent - child AND among siblings at the same time?

I have a component (LoginScreen). In that component I want to display my Login component as the first thing the user sees. When user clicks on sign up button, in my Loginscreen component, the Signup Component should be rendered instead. From the signup Component the user finds a button 'Back to Login' and when clicked, again the Login Component should be rendered insight the componentt Loginscreen. Im new to React and trying to follow tutorials about how to share data among parent/child and among siblings but am completely confused. Any help would be amazing!
class Loginscreen extends React.Component{
constructor(props) {
super(props)
this.state = {
status:false
}
this.changeStatus = this.changeStatus.bind(this);
}
changeStatus(status) {
this.setState({
status: true
});
}
render() {
return (
<div>
<Login status={this.state.status}/>
<Signup status={this.state.status}/>
<p>No account yet?</p>
<button onClick={this.changeStatus}>Sign up</button>
// if Signup Component is rendered insight Loginscreen, then this button should also not be rendered.
</div>
)
}
}
class Signup extends React.Component {
...
//where can I even call this function in my case?
handleChange() {
const status:true;
this.props.onClick(status);
}
...
<button><Link to='/loginscreen'>Back to Login</Link></button>
...
}
class Login extends React.Component {
...
...
}
Ok, I believe you are looking for routing?
Solution 1 (recommended):
Using React-Router to handle the routing and the React-Router/Link component will handle the switching.
Solution 2:
Using a simple state routing, saving the view name in the parent component and display the view based on it, also passing a function to update this view:
class App extends React.Component{
constructor(props) {
super(props)
this.state = {
view: 'login' // its login because we want it to be the default
}
this.changeView = this.changeView.bind(this);
}
changeView(view) {
this.setState({
view // ES6, if the key & value variable name the same, just type it once.
});
}
render() {
const { view } = this.state; // thats es6 destructuring, use it to make the variables clean instead of repeating the this.state/this.props
return (
<div>
{
view == 'login'
? (<Login changeView={this.changeView}/>)
: (<Signup changeView={this.changeView}/>)
}
</div>
)
}
}
class Signup extends React.Component {
...
render(){
const { changeView } = this.props;
<div className="Signup">
{/* Signup Form Here */}
<p>Already registered?</p>
{/* Wrapping the event in arrow function to avoid auto invoke */}
<button onClick={() => changeView('login')}>Login</button>
</div>
}
...
}
class Login extends React.Component {
...
render(){
const { changeView } = this.props;
<div className="Login">
{/* Login Form Here */}
<p>No account yet?</p>
<button onClick={() => changeView('signup')}>Sign up</button>
</div>
}
...
}
If there are more than 2 views you can wrap the return in a normal If statement, or move it in a separate method.
or you can use a dynamic component rendering, something like this:
render() {
const { view } = this.state;
const ViewComponent = require(`./views/${view}.jsx`);
return (<div><ViewComponent.default changeView={this.changeView} /></div>);
}

Categories