Can't get ref of underlying button? - reactjs

Using react-bootstrap: I'd like to get the DOM node of the actual button (why? because I'm using clipboardJS which requires a DOM node event handler), but this ref doesn't work:
<Button ref="myButton">Click me!</Button>
The ref returned there is to the Button component, not the actual button node. Of course I could do:
const buttonNode = ReactDOM.findDOMNode(this.refs.myButton)
But findDOMNode is going away and I'd really like to avoid that hack if possible. It would seem like react-bootstrap could use a Component-level prop to help in these situations:
<Button innerRef="myButton">Click Me!</Button> {/* wishful thinking... */}
What am I misunderstanding? Much thanks for any help.

Related

Changing the state of a React Component

Maybe it's the way I am wording things, but I am not getting the answer I want on the following topic. So currently, my code looks like this (minus all the details):
<ClickAwayListener onClickAway={handleClickAway}>
<Autocomplete options={someList} />
</ClickAwayListener>
So I have two questions.
Is Autocomplete component here considered to be an extension of ClickAwayListener? Is ClickAwayListener considered a parent component?
I want the handleClickAway function to change the options state within Autocomplete. How would I go about doing this?
I am pretty new to React, so I would appreciate any and all help. Thank you in advance.
It looks like most likely you are using your click away component not in a way it was intended to be used. It looks like it should be used with this technique.
<ClickAwayListener onClickAway={handleClickAway}>
{(isClickedAway) => (
{/*or whatever api your Autocomplete has*/}
<Autocomplete options={someList} isOpen={!isClickedAway} />
)}
</ClickAwayListener>
As to your questions:
ClickAwayListener is a parent of Autocomplete. There's no such thing as extension in react tree.
in your handleClickAway you have to change someList. If it comes through the state, then with setState, if it comes from props - with a corresponding callback.

Add Input Field onClick stateless component React Electron

I'm new to electron, and working on trying to get React/Typescript with Hooks and ApplicationContext to all come together. I didn't create the framework and I need to learn how to make this work. Just setting the context to avoid answers like, use Redux instead. :)
I have a function stateless component that is a form. It needs to be stateless so I have access to values that are held in ApplicationContext. I'm trying to render extra input fields on button click, and so far when I click the button Electron calls the method, and then re-renders. I have searched high and low, and have been banging my head on this for a few hours. I apologize in advance if there is already an answer out there.
So far the code that displays the button looks as such:
<div className="form-group">
<button onClick={() => addUrls()}>
Add a URL
</button>
</div>
And the method is just printing to the console at the moment. It looks as such:
const addUrls = () => {
console.log('clicked')
}
the print statement is getting to the console, and then Electron re-renders. The rest of the component's methods are called, and are behaving in a predictable way. I'm really confused as to why this particular action is causing renders. If anyone can point me in the direction of an answer, or point out where I am doing something dumb in my code, I would be very much grateful.
Try replacing onClick={() => addUrls()} with onClick={addUrls}. The former method will create a new reference on every render, possibly causing rerenders.
Turns out I was doing something dumb. Because my button is inside a form, and I didn't specify the button type, it was doing the default behavior of "submit" which causes a render with the onClick.
The solution was to add type="button" to the button and now it's solved.
<div className="form-group">
<button onClick={addUrls} type="button">
Add a Reference URL
</button>
</div>
I hope this can save someone some time if they come across this problem in the future.

How can I focus on a react-select component at will?

Using react-select v2, I want to show and focus on the Select element when the user hits a certain key. Following are some things I've tried or paths I've gone down.
When I set a ref to the Select element and try to call .focus on it, it says no focus function is found. Perhaps I should somehow get a child element of it and then call focus on that?
There doesn't seem to be any prop I can pass that will trigger a focus function. There is an openOnFocus but not a focusOnOpen. The only thing I can think of would be to enable autoFocus and then somehow trigger a remount but there doesn't seem to be a simple way to do this and it feels hacky. Alternatively, I could enable just create the Select component each time the key is pressed instead of showing it, then unmount it instead of hiding it.
How can I properly get the react-select element to gain focus when I want it to?
I'm using a wrapper component around my component. Here's the render method for my wrapper:
render() {
return (
<Select
options={this.props.options}
value={this.state.value}
ref={this.selectRef}
/>
);
}
And here's where I'm calling that wrapper:
<Wrapper
options={this.props.labelOptions}
ref={this.wrapperRef}
/>
I then try calling focus using either this.dropdownNode.focus() or this.dropdownNode.current.focus() and both say no focus method is found.
Because you're wrapping the Select component, you can't call Select's .focus() function from the ref you're giving to the wrapper. Since ref is a special kind of prop, the ref for that wrapper is only referring to Wrapper itself, not the component it wraps (Select).
To access the actual Select component's ref, you have to pass a ref down to it as a prop with a different, non-magic name, like innerRef (react-select code actually gives a good example of this as it's accessing the actual input element and focusing on that).
Here's the change that fixed it. Here is the wrapper component where Select is actually used (and it's taking in the ref passed to it):
render() {
return (
<Select
options={this.props.options}
value={this.state.value}
ref={this.props.innerRef}
/>
);
}
And here's the component that's calling that wrapper. In the constructor I'm creating the ref with this.selectRef = React.createRef() then I pass it in as a prop in this render method:
<Wrapper
options={this.props.labelOptions}
innerRef={this.selectRef}
/>
Then I can call focus on the Select component itself by running this.selectRef.current.focus() anywhere I want to.
Notes: Thanks to BoyWithSilverWings answer. This question pertains to React 16.3. There is also a new React.ForwardRefs method but this way seems simpler to me.

React dynamically rendered component not updating

I'm trying to render a list of components using map function in render. When I change state, render function is called as expected but my component is not updated.
I've made a sample codesandbox here
https://codesandbox.io/s/nk24mr55om
What am I doing wrong?
I've added some console logs and it look like secret content should be displayed but it is not
Once your item is pushed into the items array, it will not be updated, ever. A solution to this would be to convert the items you push into arrow functions that will get a show parameter :
items.push(show =>
<div>
{show && <div>I'm secret</div>}
My content
<br />
<input
type="button"
onClick={showSecret}
value="Show secret content"
/>
</div>
);
And then call it in the mapping of your render :
return <div key={index}>{item(show)}</div>;
However, with this solution, clicking on one button will cause every item to show their secret element, if this isn't the behavior you expect and want every item to act on its own, I suggest creating a hook for each item. And ahev them store their own show variable in their state.
I have no experience yet with react hooks, but i tried to understand whats going on.
The variable items you refer to is neither a prop nor in the state of your component, i would make the component stateful and initialize items in the state.
Maybe there is a better way with react hooks that someone else can give

Pass classname to material ui date-picker dialog

Is there a way to pass classname to matererial-ui datepicker's dialog.
http://www.material-ui.com/#/components/date-picker
Material-ui's datepicker accepts classname as a prop. But, this gets applied to the text-field upon which we want to trigger the date-dialog.
I want to pass a class attribute to the date-popup itself. Something like:
dialogClassName
The need is I want to access if the click was done somewhere inside the date-dialog and manage some other part of my code based on that. I can't figure out how to make the date-dialog accept a classname.
This issue was a bit boosting,https://github.com/mui-org/material-ui/issues/5329 but passing a dialogClassName doesn't get applied.
There is a solution which I came across by going through the DatePicker Code. You can pass the following props to the DatePicker
<DatePicker>
PopperProps={{
className: classes.desktopView,
}}
DialogProps={{
className: classes.mobileView,
}}
</DatePicker>
I'm afraid you cannot do that without modifying the DatePicket component ( at least not without some wild hacks).
If you really need this functionality you can fork material-ui repository, make the changes and submit a pull request. Or, in you project, create a local DatePicker component and do the changes there, but this approach requires you to update your version in case material-ui's version is updated.
There is no solution as of yet. However there is a workaround to pass a css class name to popup/dialog container.
Pass an img tag for rightArrowIcon props of datepicker with onload function to call its parent and inject css class.
<DatePicker
disableToolbar={true}
variant="inline"
inputVariant="outlined"
rightArrowIcon={<img src="/images/chevron-right_1.svg" id="datepicker-arrow-right" onLoad={injectTheme}/>}
leftArrowIcon={<img src="/images/chevron-right_1.svg" style={{transform:"rotate(180deg)"}}/>}
....
....
....
/>
const injectTheme=()=>{
let node = document.getElementById("datepicker-arrow-right").parentNode.parentNode.parentNode.parentNode.parentNode.parentElement;
node.classList.add("Css-Class-Name");
}

Resources