I would like to use the answer suggested here: https://stackoverflow.com/a/51065315/9973545 which involves doing
this.props.change('_validationHack', Date.now())
"in the reduxForm-wrapped component". My this.props has no change function in it, though.
I'm attempting to access the function from inside my render function which (heavily pared down) says something like:
render() {
return (<button title="Hello, World" type="Submit"
onMouseDown={() => this.props.change('_validationHack', Date.now())}
onClick={(event) => doWork(event)}
id="my-button">
my-button
</button>)
}
I assume I'm using the wrong props, but I'm not sure what other props I have. Or perhaps I need to use another library to further decorate my props?
The context in which I was making the call was incorrect, so I didn't have access to the change function. Instead, I wound up passing an helper function, prepareValidate, to the component so that change could be called in a parent context.
Related
I am using a component called DocumentPicker from the Fluent UI library.
This components has several methods:
<DocumentPicker
removeButtonAriaLabel="Remove"
onRenderSuggestionsItem={SuggestedBigItem as any}
onResolveSuggestions={ /* do some stuff here */ }
onRenderItem={SelectedDocumentItem}
getTextFromItem={getTextFromItem}
pickerSuggestionsProps={pickerSuggestionsProps}
disabled={isPickerDisabled}
inputProps={inputProps}
/>
For my specific scenario, I'd like to have a method of this component call another method. For example, have onEmptyInputFocus trigger onResolveSuggestions. How can I accomplish this?
[edit] Basically I am trying to accomplish with a function component what I would be able to do using "this" on a class component. In my class component I could write something like:
public onEmptyInputFocus () {this.onResolveSuggestions();}
Since you specify these methods, it's pretty easy:
const _onEmptyInputFocus = () => {
onResolveSuggestions()
}
<DocumentPicker
removeButtonAriaLabel="Remove"
onEmptyInputFocus={_onEmptyInputFocus}
onRenderSuggestionsItem={SuggestedBigItem as any}
onResolveSuggestions={onFilterChanged}
onRenderItem={SelectedDocumentItem}
getTextFromItem={getTextFromItem}
pickerSuggestionsProps={pickerSuggestionsProps}
disabled={isPickerDisabled}
inputProps={inputProps}
/>
I think I am pretty clear now that it cannot be accomplished with function components. You would have to know the internals of the component and tweak it.
A workaround is to use a ref and work with the underlying HTML element. In Fluent UI the prop is actually called componentRef, not just ref.
I'm trying to implement something similar to the Floating Action Button (FAB) in the Material-UI docs:
https://material-ui.com/demos/buttons/#floating-action-buttons
They have something like:
<SwipeableViews>
<TabContainer dir={theme.direction}>Item One</TabContainer>
<TabContainer dir={theme.direction}>Item Two</TabContainer>
<TabContainer dir={theme.direction}>Item Three</TabContainer>
</SwipeableViews>
{
fabs.map((fab, index) => (
<Zoom>
<Fab>{fab.icon}</Fab>
</Zoom>
));
}
I have something like:
<SwipeableViews>
<TabContainer dir={theme.direction}>
<ListOfThingsComponent />
</TabContainer>
<TabContainer dir={theme.direction}>Item Two</TabContainer>
<TabContainer dir={theme.direction}>Item Three</TabContainer>
</SwipeableViews>
{
fabs.map((fab, index) => (
<Zoom>
<Fab onClick={ListOfThingsComponent.Add???}>
Add Item to List Component
</Fab>
</Zoom>
));
}
My ListOfThingsComponent originally had an Add button and it worked great. But I wanted to follow the FAB approach for it like they had in the docs. In order to do this, the Add button would then reside outside of the child component. So how do I get a button from the parent to call the Add method of the child component?
I'm not sure how to actually implement the Add Item to List click event handler given that my list component is inside the tab, while the FAB is outside the whole tab structure.
As far as I know I can either:
find a way to connect parent/child to pass the event handler through the levels (e.g. How to pass an event handler to a child component in React)
find a way to better compose components/hierarchy to put the responsibility at the right level (e.g. remove the component and put it in the same file with this in scope using function components?)
I've seen people use ref but that just feels hacky. I'd like to know how it should be done in React. It would be nice if the example went just a bit further and showed where the event handling should reside for the FABs.
thanks in advance, as always, I'll post what I end up doing
It depends on what you expect the clicks to do. Will they only change the state of the given item or will they perform changes outside of that hierarchy? Will a fab be present in every single Tab or you're not sure?
I would think in most cases you're better off doing what you were doing before. Write a CustomComponent for each Tab and have it handle the FAB by itself. The only case in which this could be a bad approach is if you know beforehand that the FAB's callback will make changes up and out of the CustomComponent hierarchy, because in that case you may end up with a callback mess in the long run (still, nothing that global state management couldn't fix).
Edit after your edit: Having a button call a function that is inside a child component is arguably impossible to do in React (without resorting to Refs or other mechanisms that avoid React entirely) because of its one-way data flow. That function has to be somewhere in common, in this case in the component that mounts the button and the ListOfThings component. The button would call that method which would change the state in the "Parent" component, and the new state gets passed to the ListOfThings component via props:
export default class Parent extends Component {
state = {
list: []
};
clickHandler = () => {
// Update state however you need
this.setState({
list: [...this.state.list, 'newItem']
});
}
render() {
return (
<div>
<SwipeableViews>
<TabContainer dir={theme.direction}>
<ListOfThingsComponent list={this.state.list /* Passing the state as prop */}/>
</TabContainer>
<TabContainer dir={theme.direction}>Item Two</TabContainer>
<TabContainer dir={theme.direction}>Item Three</TabContainer>
</SwipeableViews>
{
fabs.map((fab, index) => (
<Zoom>
<Fab onClick={this.clickHandler /* Passing the click callback */}>
Add Item to List Component
</Fab>
</Zoom>
))
}
</div>
)
}
}
If you truly need your hierarchy to stay like that, you have to use this method or some form of global state management that the ListOfThingsComponent can read from.
If you have a component like this
class Editor extends Component {
handleChange() {
// some code
}
render() {
<div>
<input className="Editor" onChange={this.handleChange} />
</div>
}
}
Is it better to test the handle change by simulating the change event with simulate like this:
wrapper.simulate('change', { // })
Or by calling the method directly by using instance:
wrapper.instance().handleChange()
If you are using Shallow Rendering then .simulate just tries to find the right prop and call it. From the Common Gotchas section for .simulate:
Even though the name would imply this simulates an actual event, .simulate() will in fact target the component's prop based on the event you give it. For example, .simulate('click') will actually get the onClick prop and call it.
There isn't any advantage to calling .simulate when using Shallow Rendering and simply calling the prop directly avoids issues caused by the event not mapping to the correct prop.
If you are using Full DOM Rendering then .simulate will fire an event that ends up calling runEventsInBatch from the comically named ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Events.
So using .simulate with mount will actually simulate the event, just note from the Common Gotchas section that:
ReactWrapper will pass a SyntheticEvent object to the event handler in your code. Keep in mind that if the code you are testing uses properties that are not included in the SyntheticEvent, for instance event.target.value, you will need to provide a mock event...for it to work.
For Full DOM Rendering it is up to you to determine if there is any value in having ReactDOM simulate the event to call your handler or to just call your handler directly.
From this post by an Airbnb dev:
In general, I've found it's best to invoke the prop directly and avoid .simulate.
For your test it would look something like this:
test('onChange', () => {
const wrapper = shallow(<Editor />); // works with shallow or mount
const onChangeHandler = wrapper.find('input').prop('onChange');
// test onChangeHandler
})
I had a class component and then I created a button and added the onClick event to it.
Now I created a function to be called when that onClick event fires.
While referencing the function to onClick,
Why should we use something like {this.function-name} but not simply
{function-name}?
You may have many functions called function-name in different components. When you want to call them, you need to specify exactly which function you are referring to. Using the keyword this means you want to use the function-name which is attached to your current component.
this.function-name: function-name from the current component.
otherComponent.function-name: function-name from another component.
It is all about javascript scopes.
For example:
class Example extends Component {
clicked() {
console.log('clicked');
}
render() {
const innerFuncClicked = () => console.log('inner click');
return (
<button
onClick={clicked}
onClick={this.clicked}
onClick={this.clicked.bind(this)}
onClick={() => this.clicked()}
onClick={innerFuncClicked}
/>
);
}
}
As for the above code, I'll describe each "onClick" you will see.
Of course, you cant have several "onclick" as only the last one will override the rest.
1) function "clicked" is not defined in the scope so it will break
2) this will break as well as the "click" is happening inside the button component with a different "this"
3) this will work, as we bind the current "this" to the function
4) It will work as we create an arrow function that doesn't hurt the current "this"
5) this will work as the func "innerFuncClicked" exists in the scope
I would suggest you to read the basic understanding of 'this'.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
console.log(this === window); //true
Basically, this is keyword which help us to bind your custom function/ event to global 'window' events.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Not only for React, it's common for all JavaScript.
I'm trying to add a onClick listener to a li and the props will be passed to another component that will eventually call a function. My first thought was simply put 2 onClick, but I got a warning for duplicate props.
<li onClick={props.headlinesClicked} onClick={props.getData}>Top Headlines</li>
Then I tried the vahnilla JS way but I assume it's not working because these are expressions rather than actual functions.
<li onClick={() => {{props.headlinesClicked}; {props.getData}}}>Top Headlines</li>
How can I pass these multiple props in a single onClick?
Your first snippet won't work as you can't bind to a prop multiple times. The second is incorrect firstly because anything inside the brackets is JS, so the additional brackets inside are unnecessary and you're also not invoking any of the functions.
Think of it this way, onClick={props.headlinesClicked} is kind of like a shorthand for onClick={() => props.headlinesClicked()} (not exactly as onClick also passes an event as the first parameter, but hopefully you get the gist of it). So, when you do onClick={() => props.headlinesClicked} you're not actually calling the function, just returning a reference to it which then doesn't actually do anything.
You could either do onClick={() => props.headlinesClicked(); props.getData()}, or extract it to a new function/method that does both calls, then invoke it with onClick={myNewMethod} or onClick={() => myNewMethod()}.
Have the passed function call the other two functions normally:
<li onClick={() => { props.headlinesClicked(); props.getData() }}>Top Headlines</li>
Alternatively (recommended for readability), make a new function which calls those two functions. Consider making the function a class method if it's a class component.
const handleListItemClick = () => {
props.headlinesClicked()
props.getData()
}
return <li onClick={handleListItemClick}>Top Headlines</li>
I have recommended below way it will work 100%
const handleListItemClick = () => {
if(props.headlinesClicked){
props.headlinesClicked()
}else{ props.getData() }
return <li onClick={handleListItemClick}>Top Headlines</li>
Note:react have confuse two props we mentioned same onClick .