Trying to invoke modal function from outside, but not possible. Achieved with class component using static method and properties, but this will not work there. Observers are giving some issues, finally landing to stateless component. How best way we can make this work?
//Inside
import Modal from './modal';
// Not working
<Button onClick={Modal.showModal}
<Modal />
//Outside
export const Modal = () => {
const [visible, setVisible] = useState(false);
const showModal = () => {
setVisible(true);
}
return(
<Dialog visible={visible}>Hello Test</h1>
)
}
You can declare setVisible in parent component:
const [visible, setVisible] = useState(true);
Then pass the set function to Modal:
<Modal visible={visible} setVisible={setVisible} />
Set visible from outside:
<button onClick={() => setVisible(!visible)}>Click</button>
Sample: https://codesandbox.io/s/competent-bassi-x3dqd?file=/src/App.js:267-325
You will need to "lift the state up" to a component that will pass the state and the state handlers to his children, or use some global state with redux or context.
Example:
<App>:
import Modal from "./modal";
import { useState } from "react";
const [visible, setVisible] = useState(false);
const showModal = () => {
setVisible(true);
};
return (
<>
<Button onClick={showModal}> Hide Modal </Button>
<Modal visible={visible} />
</>
);
<Modal>:
export default const Modal = ({ visible }) => {
return (
<Dialog visible={visible}>
<h1>Hello Test</h1>
</Dialog>
);
};
Also, by only using the export keyword, you can import only single export from a module, therefore you can't use the default import syntax import Modal from "..."
and you will be required to import as such - import { Modal } from '...'
I managed to solve usingEffect here, but not sure if this is perfect for my implementation. I am trying to update the state for already mounted component.
useEffect(() => {
if (props.visible) {
showModal();
}
}, [props.visible]);
And handled resetState with a callback prop
Related
I have two component. CourseInput.js and Button.js. In the CourseInput.js component I create a React form and inside that form I use Button component. I pass type as a props only. But in the Button.js component how I can access onClick function though I haven't pass it in Button.js component. How Button.js component is getting onClick function as props in onClick= {props.onClick}.
CourseInput.js
import React, { useState } from "react";
import Button from "../../UI/Button/Button";
import "./CourseInput.css";
const CourseInput = (props) => {
console.log(props);
const [enteredValue, setEnteredValue] = useState("");
const goalInputChangeHandler = (event) => {
setEnteredValue(event.target.value);
};
const formSubmitHandler = (event) => {
event.preventDefault();
props.onAddGoal(enteredValue);
};
return (
<form onSubmit={formSubmitHandler}>
<div className="form-control">
<label>Course Goal</label>
<input type="text" onChange={goalInputChangeHandler} />
</div>
<Button type="submit">Add Goal</Button>
</form>
);
};
export default CourseInput;
Button.js
import React from "react";
import "./Button.css";
const Button = (props) => {
return (
<button type={props.type} className="button" onClick={props.onClick}>
{props.children}
</button>
);
};
export default Button;
*My code is working as expected but how this props.onClick works? *
Your onClick is undefined for the button and since its JS, it won't complain, even if you remove it, the code will work. When you type the button as submit, it will just invoke the onSubmit function on the form.
I am using context API in react for managing state. For this I have created a file AppContext.js where I have created context and Provider:
import { useState, createContext } from "react";
export const AppContext = createContext();
export const AppProvider = (props) => {
const [appdata, setAppdata] = useState({
data1: "this is data1",
data2: "this is data2",
});
return (
<AppContext.Provider value={[appdata, setAppdata]}>
{props.children}
</AppContext.Provider>
);
};
I have imported this Provider in the parent component of the app i.e App.js. Also I have wrapped the <AppChild/> component in the Provider.
import AppChild from "./AppChild";
import { AppProvider } from "./AppContext";
const App = () => {
return (
<AppProvider>
<div className="App">hello</div>
<AppChild />
</AppProvider>
);
};
export default App;
Now from AppChild component, I only needed to update the data1 key of my state. For this I have created a button with a onClick through which I will be changing my state. I have used to following code in AppChild.js for this:
import { useContext } from "react";
import { AppContext } from "./AppContext";
const AppChild = () => {
const [appdata, setAppdata] = useContext(AppContext);
return (
<div>
<h3 style={{ color: "red" }}>Data for Appchild: {appdata.data1}</h3>
<button
onClick={() =>
setAppdata((prev) => {
prev.data1 = "updated data1";
return prev;
})
}
>
click to change
</button>
</div>
);
};
export default AppChild;
But when I click the button, the text inside the <h3> block is not changing. Although when I change the whole state by passing the whole object directly to setAppdata as shown below,
This way the state updates successfully. Why is the first method not working where I only wanted to change the data1 key?
You are updating state in wrong way so it is not working. This is how you should update state
<button
onClick={() =>
setAppdata((prevState) => ({
...prevState,
data1: "Your New Data"
}))
}
>
click to change
</button>
I am trying to test a react component using Enzyme. I am not able to test the click on the IconButton component and the function doesn't get called when i simulate a click.
This is how IconButton is defined on an external component.
var IconButton = function (props) {
return (React.createElement(IconButton$1, { color: 'default', onClick: props.onClick, disabled: props.disabled, size: props.size, onMouseDown: props.onMouseDown }, props.children));
};export{Button,IconButton};
This is how I am using it in my app.
import React, {useState, useEffect} from 'react';
import { Drawer } from '#material-ui/core';
import ExpandLessIcon from '#material-ui/icons/ExpandLess';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import { IconButton } from '#mycomponent/button';
export default function Component1 {
const classes = useStyles();
const [open, setOpen] = useState(true);
const handleClick = function (event) {
if (event) {
setOpen(!open);
}
else {
return;
}
};
return (
<Drawer>
<div className="classname1">
<IconButton onClick={(e) => handleClick(e)} className={classes.button, "iconBtn"}>
{open ? <ExpandLessIcon data-test="lessIcon" /> : <ExpandMoreIcon data-test="moreIcon" />}
</IconButton>
</div>
</Drawer>
);
}
Here is my test for simulating the click on the Icon Button. I also tried another way to check that the handleClick was getting called but it still fails.
const wrapper = shallow(<Component1 />);
it('Test the button click', () => {
expect(wrapper.containsMatchingElement(<ExpandMoreIcon />)).toBeTruthy()
const element = wrapper.find(".iconBtn")
const mockEvent = {target: {}};
element.simulate('click', mockEvent)
expect(wrapper.containsMatchingElement(<ExpandLessIcon />)).toBeTruthy()
})
Try changing this line:
const element = wrapper.find("button").at(0);
or you could find it by it's className from debug():
const element = wrapper.find(".MuiButtonBase-root MuiIconButton-root");
Notice that you'd simulate a click on an actual html button in such case.
Please see this codesandbox. I am trying to create a react HOC that receives both a functional component and a function that gets invoked in the HOC.
To do this, I need to create a react component that returns this HOC. The function that needs to be invoked in the HOC needs to share it's scope with the react component, since it might do things like change the component's state or make api calls. My attempt at this is is trying to create a nested react component, but when I do, I get the following error:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it in SampleComponent
How can I create a react HOC that accepts both a react component and a function to be invoked? Thanks! The code can also be found below:
index.tsx:
import * as React from "react";
import { render } from "react-dom";
import SampleComponent from "./sampleComponent";
render(<SampleComponent />, document.getElementById("root"));
sampleWrapper.tsx:
import * as React from "react";
const sampleWrapper = (
WrappedComponent: React.FC,
onButtonClick: () => void
): React.FC => {
const Component: React.FC = () => (
<div>
<button onClick={onButtonClick} type="button">
Click Me
</button>
<WrappedComponent />
</div>
);
return Component;
};
export default sampleWrapper;
sampleComponent.tsx:
import * as React from "react";
import sampleWrapper from "./sampleWrapper";
const SampleComponent: React.FC = () => {
const [title, setTitle] = React.useState("hello world");
const handleTitleChange = (): void => {
setTitle("This is a new title");
};
const SampleInnerComponent: React.FC = () => <h1>{title}</h1>;
return sampleWrapper(SampleInnerComponent, handleTitleChange);
};
export default SampleComponent;
As your sampleWrapper returns a functional component. What you need to do is save the returned functional component into a variable and render the component the same way you do functional component. i.e
import * as React from "react";
import sampleWrapper from "./sampleWrapper";
const SampleComponent: React.FC = () => {
const [title, setTitle] = React.useState("hello world");
const handleTitleChange = (): void => {
setTitle("This is a new title");
};
const SampleInnerComponent: React.FC = () => <h1>{title}</h1>;
const ReturnedSampleComponent = sampleWrapper(SampleInnerComponent, handleTitleChange);
return <ReturnedSampleComponent />;
};
export default SampleComponent;
You can check this codesandbox
It seems you are not returning the React Element, but the Component. You want to return the element React.ReactElemnt. This is what you want i think:
const sampleWrapper = (
WrappedComponent: React.FC,
onButtonClick: () => void
): React.FC => {
return (<div>
<button onClick={onButtonClick} type="button">
Click Me
</button>
<WrappedComponent />
</div>);
};
another alternative :
import * as React from "react";
const sampleWrapper = (
WrappedComponent: React.FC,
onButtonClick: () => void
): React.FC => {
const Component: React.FC = () => (
<div>
<button onClick={onButtonClick} type="button">
Click Me
</button>
<WrappedComponent />
</div>
);
return <Component/>;
};
export default sampleWrapper;
I'm new to Redux.
I handled the basic Facebook Flux architecture very easily and made some nice app with it.
But I struggle very hard to get very simple Redux App to work.
My main concern is about containers and the way they catch events from components.
I have this very simple App :
CONTAINER
import { connect } from 'react-redux'
import {changevalue} from 'actions'
import App from 'components/App'
const mapStateToProps = (state) => {
return {
selector:state.value
}
}
const mapDispatchToProps = (dispatch) => {
return {
onClick: (e) => {
console.log(e)
dispatch(changeValue())
}
}
}
const AppContainer = connect(
mapStateToProps,
mapDispatchToProps
)(App)
export default AppContainer;
Component
import React, {Component} from 'react'
import Selector from 'components/Selector'
import Displayer from 'components/Displayer'
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick}/>
<Displayer />
</div>
)
export default App;
CHILD COMPONENT
import React, {Component} from 'react'
const Selector = ({onClick}) => (
<div onClick={onClick}>click me</div>
)
export default Selector;
onClick event does not reach the container's mapDispatchToProps.
I feel that if I get this work, I get a revelation, and finally get the Redux thing! ;)
Can anybody help me get this, please ? (The Redux doc is TOTALLY NOT helpfull...)
The problem is in the App component. In the onClick property of the Selector component, you're passing a function which returns the definition of a function, not the result.
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick}/> // here is the problem
<Displayer />
</div>
)
You should simply do this instead:
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick(e)}/>
<Displayer />
</div>
)
Or even simpler:
const App = (selector, onClick) => (
<div>
<Selector onClick={onClick}/>
<Displayer />
</div>
)