I have a functional component
const text = ({data}) => {
return (
<p onClick={()=> render more?}>info</p>
)}
const more = ({data}) => {
return (<p>..........</p>)
}
Is it possible to render more component on the onClick event?
Sure, you'll need a state variable. Use the state to determine whether to render more or not, and then set the state when the click happens. If you have react 16.8 or later, you can do this in a functional component with hooks:
import { useState } from 'react';
const MyComponent = ({data}) => {
const [showMore, setShowMore] = useState(false);
return (
<div>
<p onClick={() => setShowMore(true)}>info</p>
{showMore && <More data={data} />}
</div>
)}
}
Prior to 16.8, you'll need to use a class component.
class MyComponent extends React.Component {
state = {
showMore: false,
}
render() {
return (
<div>
<p onClick={() => this.setState({ showMore: true})}>info</p>
{this.state.showMore && <More data={this.props.data} />}
</div>
)}
}
}
Related
const Section = ({ text, changeFn }) => {
return(
<>
<Content text={text} changeFn={changeFn}/>
<Content text={text} changeFn={changeFn}/>
<Content text={text} changeFn={changeFn}/>
</>
)
}
const Content = ({ changeFn, text }) => {
const [ textCont, setText ] =useState('text')
return(
<>
<div>{textCont}</div>
<button onClick={changeFn}>Edit</button>
</>
)
}
const Form = ({ changeFn, setContent }) => {
let inputRef = useRef()
const applyChangefn = () => {
setContent(inputRef.current.value)
changeFn()
}
return(
<>
<input ref={inputRef}/>
<button onClick={applyChangefn}>Save</button>
</>
)
}
I want to make a component which exists as example with its own usestate in Content component.
Then i want to send it multiplied by 3 times into Section component.
First question is how to send props between sibling components
Second question is how to make 3 same components which have its own useState state. I mean 3 same components with independent usestate.
Here is courseButton.jsx:
import React from "react";
import styles from "./styles.module.scss";
import { MenuFoldOutlined, MenuUnfoldOutlined } from "#ant-design/icons";
export default (props) => {
const { collapsed, onClick } = props;
return <>{collapsed ? MenuUnfoldOutlined : MenuFoldOutlined}</>;
};
Both of my components have the same props. So I want to avoid coding like this:
import React from "react";
import styles from "./styles.module.scss";
import { MenuFoldOutlined, MenuUnfoldOutlined } from "#ant-design/icons";
export default (props) => {
const { collapsed, onClick } = props;
return (
<>
{collapsed ? (
<MenuUnfoldOutlined className={styles.trigger} onClick={onClick} />
) : (
<MenuFoldOutlined className={styles.trigger} onClick={onClick} />
)}
</>
);
};
So how I can give the selected component the style in one line code.
I want something like this code.
This solution scales better as we assign props only once.
export default (props) => {
const { collapsed, onClick } = props;
const Component = collapsed ? MenuUnfoldOutlined : MenuFoldOutlined;
return <Component className={styles.trigger} onClick={onClick} />;
};
If I understood you correctly, you want to keep your code DRY. You can store your props in a variable to keep it reusable.
export default (props) => {
const {collapsed, onClick} = props;
const genericProps = {
className: styles.trigger,
onClick,
}
if (collapsed) {
return <MenuUnfoldOutlined {...genericProps} />
}
return <MenuFoldOutlined {...genericProps} />
}
Note: React Fragment is redundant.
you can simply write it like this:
import React from "react"
import styles from "./styles.module.scss"
import {MenuFoldOutlined, MenuUnfoldOutlined} from '#ant-design/icons'
export default (props) => {
const {collapsed, onClick} = props
return (
<>
{collapsed ? <MenuUnfoldOutlined {...props} /> : <MenuFoldOutlined {...props}/>}
</>
)
}
This is the same as writing .
If the wrapping component has a bunch of props and you only need specific props you can try the following approach:
export default (props) => {
// If props has a bunch of props but we only need collapsed and
// onClick:
const {collapsed, onClick} = props
const menuProps = {collapsed, onClick}
return (
<>
{collapsed ? <MenuUnfoldOutlined {...menuProps} /> : <MenuFoldOutlined {...menuProps}/>}
</>
)
}
I am using Context API to use themes in my React Native project. To consume the Theme Context, I made a Higher Order Component and passed them into the component as props.
This worked fine for the most of the app. But when I started using refs, it started crashing, because as per the documentation, the refs will not be forwarded automatically, and we need to use the React.forwardRef API. But something when wrong with my implementation and I am not able to resolve it.
Here's the code I have been working on:
// Higher order component, to wrap my component with theme (withTheme.js)
export const withTheme = (Component, areRefsUsed = false) => {
// Logic to check if refs are being used
if (areRefsUsed) {
const ThemedComponent = (props, ref) => {
return (
<ThemeContext.Consumer>
{theme => (<Component {...props} theme={theme} ref={ref} />)}
</ThemeContext.Consumer>
)
};
return React.forwardRef(ThemedComponent);
}
return (props) => {
return (
<ThemeContext.Consumer>
{theme => <Component {...props} theme={theme} />}
</ThemeContext.Consumer>
)
}
};
// My component where I am using refs (ExampleComponent.js)
class ExampleComponent extends Component {
constructor(props) {
super(props);
this.refForSomeComponent = React.createRef();
}
render() {
const { theme } = this.props;
return (
<Fragment>
<Text style={{color: theme.primaryColor}}>It is working</Text>
<TextInput ref={this.refForSomeComponent} value={'Test'} />
</Fragment>
)
}
}
export default withTheme(ExampleComponent, true);
When I try to run the app, this error is being thrown:
The component for route 'ExampleComponent' must be a React component.
I am using React-Router v4 to navigate in my React app. The following is a component wrapped in the withRouter() function to make it able to change route on click:
const LogoName = withRouter(({history, props}) => (
<h1
{...props}
onClick={() => {history.push('/')}}>
BandMate
</h1>
));
As you can see I pass the props to the component, which I need in order to change the class of the component. The problem here is that props is undefined in the <LogoName> component. I need to be able to change the class of this component when I click on another component, like this:
<LogoName className={this.state.searchOpen ? "hidden" : ""} />
<div id="search-container">
<SearchIcon
onClick={this.handleClick}
className={this.state.searchOpen ? "active" : ""} />
<SearchBar className={this.state.searchOpen ? "active" : ""}/>
</div>
Here is how I handle the click. Basically just setting the state.
constructor(){
super();
this.state = {
searchOpen: false
}
}
handleClick = () => {
this.setState( {searchOpen: !this.state.searchOpen} );
}
Is there a way for me to pass props to a component that is wrapped inside the withRouter() function or is there a similar way to create a component which has the ability to navigate with React-Router and still receive props?
Thanks in advance.
The problem is that while destructuring, you want to destructure props but you are not passing any prop named props to LogoName component
You can change your argument to
const LogoName = withRouter((props) => (
<h1
{...props}
onClick={() => {props.history.push('/')}}>
BandMate
</h1>
));
However you can still destructure the props like #Danny also suggested by using the spread operator syntax like
const LogoName = withRouter(({history, ...props}) => (
<h1
{...props}
onClick={() => {history.push('/')}}>
BandMate
</h1>
));
You're close, just spread the props in your function signature as well:
const LogoName = withRouter(({ history, ...props }) => (
<h1
{...props}
onClick={() => {history.push('/')}}>
BandMate
</h1>
));
This worked for me:
import {withRouter} from 'react-router-dom';
class Login extends React.Component
{
handleClick=()=>{
this.props.history.push('/page');
}
render()
{
return(
<div>
.......
<button onClick={this.handleClick()}>Redirect</button>
</div>);
}
}
export default withRouter(({history})=>{
const classes = useStyles();
return (
<Login history={history} classes={classes} />
)
});
So, I have simple code (class) like this:
export default class LoginAction {
isLoggedIn = () => {
return true
}
}
And I used it in my other classes like this:
export default class Main extends Component {
render = () => {
const loginAction = new LoginAction()
if (loginAction.isLoggedIn()) {
return (
<View style={{ flex: 1 }}>
<Header headerText={'Post List'} />
<PostList />
</View>
)
}
....... (split)
}
}
The question is, when I change the return value on the isLoggedIn function, why Main component not re-rendered?
It's React Native, and I use Hot Reloading.
A component re-renders only in 2 situations:
if its state has changed
if the received props have changed
In your Main component, none of these situations happen.
To fix it, you could pass isLoggedIn to your component:
// index.js
const loginAction = new LoginAction()
let isLoggedIn = loginAction.isLoggedIn()
const setLoggedUser = user => {
loginAction.setLoggedUser(user)
isLoggedIn = true
}
ReactDOM.render(
<div>
{!isLoggedIn && <Login setLoggedUser={setLoggedUser} />}
<Main isLoggedIn={isLoggedIn} />
</div>,
document.getElementById('root')
)
And use this prop in your component's render:
export default class Main extends Component {
render = () => {
if (this.props.isLoggedIn) {
return (
<View style={{ flex: 1 }}>
<Header headerText={'Post List'} />
<PostList />
</View>
)
}
...
}
}
In doing so, your component will re-render when isLoggedIn changes.