I have different jsx files.I want to access Menu.jsx component function in Header.jsx function to open menu. I am using Material-UI also. So Here I have a function "handleToggle" in Menu.jsx and I want to trigger this function from a button "onLeftIconButtonTouchTap" which is available in Header.jsx. How can I access component internal function from any other component, should I need to maintain any hierarchy?
App.jsx
export default class App extends React.Component{
render(){
return(
<main>
<Menu/>
<Header/>
<Body/>
<Footer/>
</main>
)
}
}
Header.jsx
export default class Header extends BaseMUI{
render(){
return (
<header>
<AppBar
title="title"
onLeftIconButtonTouchTap={this.handleToggle}
iconClassNameRight="muidocs-icon-navigation-expand-more"
/>
</header>
)
}
}
Menu.jsx
export default class Menu extends BaseMUI{
constructor(props) {
super(props);
this.state = {
open: false
};
}
handleToggle = () => this.setState({open: !this.state.open});
handleClose = () => this.setState({open: false});
componentDidMount(){
console.log(this.refs);
}
render(){
return (
<nav>
<RaisedButton
label="Open Drawer"
onTouchTap={this.handleToggle}/>
<Drawer
docked={false}
width={200}
open={this.state.open}
ref="drawer"
onRequestChange={(open) => this.setState({open})}>
<MenuItem onTouchTap={this.handleClose}>Menu Item</MenuItem>
<MenuItem onTouchTap={this.handleClose}>Menu Item 2</MenuItem>
</Drawer>
</nav>
)
}
}
You need to create parent component, store there state of opening menu, and change props of the menu - here an example, how it can be implemented in only react, without Flux and Redux, will be more right to use them, if you have many idential situations.
class MenuContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
isMenuOpen = false
}
}
shouldComponentUpdate(newProps, newState) {
return newState.isMenuOpen != this.state.isMenuOpen
}
openMenu = (isMenuOpen ) => {
this.setState({isMenuOpen : isMenuOpen });
}
render () {
return (
<Header : isMenuOpen={this.state.isMenuOpen}
openMenu= {this.openMenu}/>
<Menu isMenuOpen={this.state.isMenuOpen})
}
}
And then in your menu component
shouldComponentUpdate(newProps) {
return this.props.isMenuOpen != newProps.isMenuOpen
}
Since you Header.jxs and Menu.jsx are in same hierarchy, you can solve in 2 ways...
1) you can fire a event from Header.jxs and listen for the action in Menu.jsx
2) Using react-redux way, fire the action on click and observer the props changes in Menu.jsx
Related
I am seeing strange behavior that I don't understand in the following code
export class PlayerListPage extends React.Component<PlayerListPageProps, {toggle: boolean}> {
constructor(props: PlayerListPageProps) {
super(props);
this.state = {
toggle: false
};
}
handleToggle = () => {
this.setState({
toggle: !this.state.toggle
})
}
render() {
return (
<div>
<TextField
key={1}
label={'label'}
value={'value'}
/>
<Button
onClick={this.handleToggle}
>
Click
</Button>
</div>
)
}
}
Every time this toggle method is triggered, and updates the component's state, the TextField component is unmounted/remounted instead of just re-rendering.
I would expect TextField to re-render without unmounting first. Could somebody kindly point out what I am doing wrong?
Some additional context: here is the parent component (which is the root of the app):
#observer
export class RootView extends React.PureComponent {
private rootStore: RootStore = new RootStore();
private appTheme : any = createMuiTheme(DarkTheme);
#observable private player: Player
async componentDidMount() {
await this.rootStore.playerStore.retrievePlayerBios();
this.player = this.rootStore.playerStore.getPlayer('12');
}
render() {
console.log(this.appTheme);
return (
<main>
<MuiThemeProvider theme={this.appTheme}>
<Provider rootStore={this.rootStore}>
{/* {!!this.player && <PlayerStatsView player={this.player}/>} */}
<PlayerListPage/>
</Provider>
</MuiThemeProvider>
</main>
)
}
}
I'm trying to call child component from parent component in reactjs using refs.but it throws error saying showModal() is not a function when I tried to call.
//app.js
class app extends Component {
constructor(props) {
super(props);
this.POPUP = React.createRef();
}
showModal(){
this.POPUP.showModal(true);
}
render() {
return (
<React.Fragment>
<span><a onClick={() => this.showModal()}>Show</a></span>
<POPUP onRef={ref => (this.POPUP = ref)}></POPUP>
</React.Fragment >
)
}
}
popup.js
class POPUP extends Component {
showModal(show) {
console.log('showmodal');
}
render() {
console.log(this.props.showModalPopup);
<React.Fragment>
<Modal
position="center">
<div>
//code
</div>
</Modal>
</React.Fragment>
)
}
}
Is there any alternative in nextjs.please help
https://reactjs.org/docs/refs-and-the-dom.html#accessing-refs
First of all if you want to access that POPUP instance you should do
this.POPUP.current.showModal(true);
BTW Your showModal function needs to be bound to the child component if you intend to alter its state.
However, even this is doable - this is usually not the recommended way of doing React.
If you want the parent to decide if showModalPopup should be true, you probably should keep the state inside of your parent component:
class App extends Component {
constructor(props) {
super(props);
this.state = { showModalPopup: false };
this.showModal = this.showModal.bind(this);
}
showModal(){
this.setState({ showModalPopup: true });
}
render() {
return (
<React.Fragment>
<span><a onClick={this.showModal}>Show</a></span>
<POPUP show={this.state.showModalPopup}></POPUP>
</React.Fragment >
)
}
}
const POPUP = ({ show }) => (
<Modal show={show} position="center">
// your content.
</Modal>
)
I have a Modal component in the Main.js app and I want to trigger it from a different component (in this case Homepage, but I have one component for each page).
I don´t know how to pass a component to be rendered inside the modal.
If it helps I´m using Context API.
App.js
const App = () => {
return (
<div>this is the main app</div>
<Modal />
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Modal.js
class Modal extends Component {
constructor(props) {
super(props)
this.state = {
'open': false
}
}
componentWillReceiveProps(nextProps) {
this.setState({open: nextProps.open})
}
render() {
if (!this.state.open) {
return false
}
return(
<div className="modal">
{this.props.children}
</div>
)
}
}
export default Modal
Homepage.js
class Homepage extends Component {
constructor(props) {
super(props)
this.handleOpenModal = this.handleOpenModal.bind(this)
}
handleOpenModal() {
// here I want to open the modal and pass the <ModalContent /> component
// basically call the <Modal open="true"> from the Main component
}
render() {
return(
<div className="homepage">
<button onClick={this.handleOpenModal}>open the modal</button>
</div>
)
}
}
const ModalContent = () => {
return(
<div>this is the content I want to render inside the modal</div>
)
}
thank you.
I strongly recommend using something like react-modal (npm). It allows you to keep modal content right next to the trigger. It does this by appending a "portal" high up in the DOM and handles appending the content to is.
Your example may look like the following:
import Modal from 'react-modal';
class Homepage extends Component {
constructor(props) {
super(props)
this.state = { modalOpen: false };
this.handleOpenModal = this.handleOpenModal.bind(this)
}
handleOpenModal() {
this.setState({ modalOpen: true });
}
render() {
return(
<div className="homepage">
<button onClick={this.handleOpenModal}>open the modal</button>
<Modal open={this.state.modalOpen}>
<ModalContent />
</Modal>
</div>
)
}
}
const ModalContent = () => {
return(
<div>this is the content I want to render inside the modal</div>
)
}
Can I use children in React Container or is it wrong?
For example, I have a list of buttons(ActionButton) that are grouped together (ActionMenu).
import React from 'react';
class App extends React.Component {
render() {
return (
<ActionMenu>
<ActionButton name="New" icon="add" />
<ActionButton name="Delete" icon="remove" />
</ActionMenu>
)
}
}
class ActionMenu extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
alert('Click!');
}
render() {
return React.Children.map(this.props.children, (button) =>
React.cloneElement(button, {
onClick: this.handleClick
})
);
}
}
function ActionButton({ name, icon, onClick }) {
return <button class={icon} onClick={onClick}>{name}</button>
}
You can use children regardless of whether it's a component of container.
"[children are] especially common for components like Sidebar or Dialog that represent generic 'boxes'."
In your case you have a menu, which falls into this category.
https://reactjs.org/docs/composition-vs-inheritance.html
I think this is what you are after. Actually you should just put the children in its closest parent instead of its grandpa.
import React from 'react';
import { render } from 'react-dom';
function ActionButton({ name, handleClick }) {
return <button onClick={handleClick}>{name}</button>
}
class ActionMenu extends React.Component {
constructor(props) {
super(props);
}
handleClick = () => {
alert('Click!');
}
render() {
return (
<div>
<ActionButton name="add" handleClick={this.handleClick}/>
<ActionButton name="remove" handleClick={this.handleClick} />
</div>
);
}
}
class App extends React.Component {
render() {
return (
<ActionMenu />
)
}
}
render(<App />, document.getElementById('root'));
You can try to run it in sandbox.
By the way, using bind is quite redundant now, we can use public class fields syntax, which is already ECMA stage 2.
I've been struggling with the concept of overriding the behaviour of components between sibling components (components that do not have a parent-child relationship).
I have an App component that renders a header and a content component. The header contains a button that will let a user navigate back (not for real in this example). Now I want the content component to override the back button's behaviour, for example if a user is editing a form and I want to pop up a modal.
The reason I want to do this is because I want to (optionally) control navigation from within the content component itself.
I have found this (see snippet) solution, but I feel like it's not the right way to handle this. I would like to have some advice on how to handle this situation.
A few side-notes:
I'm actually building a react-native app, but in the hope of reaching more people I've simplified it down to a react example. I'm using NavigatorExperimental for navigation.
I am using redux/redux-form
Any help is appreciated.
class NavigationHeader extends React.Component {
render() {
return (
<span onClick={this.props.goBack}>Go back!</span>
);
}
}
class Content extends React.Component {
componentDidMount() {
this.props.setBackButtonBehaviour(() => console.log("BackButton overridden from Content"));
}
componentWillUnmount() {
this.props.resetBackButtonBehaviour();
}
render() {
return (<div style={{background: "red"}}>Content</div>);
}
}
class Navigator extends React.Component {
constructor(props) {
super(props);
this.state = {
overrideHeaderBackButtonBehaviour: null
}
}
setBackButtonBehaviour(func) {
this.setState({overrideHeaderBackButtonBehaviour: func});
}
resetBackButtonBehaviour() {
this.setState({overrideHeaderBackButtonBehaviour: null});
}
defaultBackButtonBehaviour() {
console.log("Default back-button behaviour");
}
render() {
return (
<div>
<NavigationHeader goBack={this.state.overrideHeaderBackButtonBehaviour || this.defaultBackButtonBehaviour} />
<Content
setBackButtonBehaviour={this.setBackButtonBehaviour.bind(this)}
resetBackButtonBehaviour={this.resetBackButtonBehaviour.bind(this)}
/>
</div>
);
}
}
ReactDOM.render(<Navigator />, 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">
</div>
You can try something like this:
Header.js
import actions from './actions';
#connect(
// map state to props
(state, props) => ({
canNavigate: state.navigation.canNavigate,
}),
// map dispatch to props
{
goBack: actions.goBack,
showNavigationAlert: actions.showNavigationAlert,
}
)
class Header extends React.Component {
render() {
return (
{/* if 'canNavigate' flag is false and user tries to navigate, then show alert */}
<span
onClick={this.props.canNavigate ? this.props.goBack : this.props.showNavigationAlert}
>
Go back!
</span>
);
}
}
Content.js
import actions from './actions';
#connect(
// map state to props
(state, props) => ({
navigationAlert: state.navigation.navigationAlert,
}),
// map dispatch to props
{
toggleNavigation: actions.toggleNavigation,
}
)
class Content extends React.Component {
render() {
return (
<div>
{/*
Show modal if 'navigationAlert' flag is true;
From the modal, if user choose to leave page then call toggleNavigation.
You may call the corresponding navigation function after this (eg: goBack)
*/}
{this.props.navigationAlert && <Modal toggleNavigation={this.props.toggleNavigation} />}
{/* eg: disable navigation when clicked inside the div */}
<form onClick={this.props.toggleNavigation}>
Content click!
</form>
</div>
);
}
}
App.js
class App extends React.Component {
render() {
return (
<div>
<Header />
<Content />
</div>
);
}
}
navigationReducer.js
const initialState = {
canNavigate: true,
navigationAlert: false,
};
export function navigationReducer(state = initialState, action = {}) {
let newState = {};
switch (action.type) {
case TOGGLE_NAVIGATION:
return { ...state, canNavigate: !state.canNavigate };
case TOGGLE_NAVIGATION_ALERT:
return { ...state, navigationAlert: !state.navigationAlert };
default:
return state;
}
}
actions.js
export function toggleNavigation(gdsSession) {
return { type: TOGGLE_NAVIGATION };
}
export function toggleNavigationAlert(gdsSession) {
return { type: TOGGLE_NAVIGATION_ALERT };
}
// ... other actions
and render into DOM.
ReactDOM.render(<App />, document.getElementById("app"));