How to change attribute of a React Element - reactjs

I've created a render method which adds a number of dynamically created 'InfoWindow' elements to a Class based object.
Each InfoWindow element has a unique ID and key.
I also have a number of 'Marker' elements with corresponding ids and keys.
Currently all Infowindows have a prop of 'visible={false}'
When I click a Marker a function is called which outputs the Marker ID.
I'd like to find the InfoWindow with the relevant ID and set visibility = {true}
Is there a way to find the relevant InfoWindow element using its key or ID, and then call setAttribute (or equivalent)?
I've tried searching the DOM for the ID but Google Maps doesn't render that way, so I'm thinking there must be a more React-y way to do this?
let visibilityFunction = () => {
this.changeVisibility(01);
};
changeVisibility = (e) => {
console.log(e);
//this currently outputs the ID (01)
}
render() {
return(
<Parent>
<InfoWindow
visible={false}
key={01-iw}
id={01-iw}
/>
<Marker
key={01}
id={01}
onClick={visibilityFunction}
/>
</Parent>
);
}

Like I was saying in the comments. Use state here to update the visibility.
class MyComponent extends React.Component {
state = { visibleWindows: {}, currentWindows: [1] };
changeVisibility = id => {
this.setState(prevState => ({
visibleWindows: {
...prevState.visibleWindows,
[id]: !prevState.visibleWindows[id]
}
}));
};
render() {
const { currentWindows, visibleWindows } = this.state;
return (
<div>
{currentWindows.map(win => (
<ChildWindow key={win} id={win} isVisible={!!visibleWindows[win]} onChange={this.changeVisibility} />
))}
</div>
);
}
}
class ChildWindow extends React.Component {
changeVisibility = () => {
this.props.onChange(this.props.id)
}
render() {
<React.Fragment>
<InfoWindow
visible={this.props.isVisible}
key={`${win}-iw`}
id={`${win}-iw`}
/>
<Marker
key={win}
id={win}
onClick={this.changeVisibility}
/>
</React.Fragment>
}
}
Here's a rudimetary example for you to poke around with :)

Related

How to load React Component by variable?

I have multiple components exported in a file 'buildings.js'
For the sake of simplicity I'll just give a basic example
export const NewBuilding = () => {
return (
<div className="wrapper">New Building</div>
)
}
export const Barracks = () => {
return (
<div className="wrapper">Barracks</div>
)
}
Another component get the specific building component name by props. I need to render the building whose name matches the name in the props.
class BuildingContent extends React.Component {
getComponent = () => {
var name = this.props.content.name;
// Here I want to access the component by variable
// Like <Buildings[name] /> or anything similar that works.
// This obviously doesn't work
//return <Buildings.Name />
return <Buildings.Barracks />
}
render() {
return (
<div className='building-content-wrapper'>
// Hardcoded for functionality
<Buildings.NewBuilding />
</div>
)
}
}
You can create an object of multiple Components and its key should be the name you are passing in props like
const allComponents = {
newBuilding: NewBuilding,
barracks: Barracks,
}
class BuildingContent extends React.Component {
let name = this.props.content.name;
let Building = allComponents[name];
render() {
return (
<div className='building-content-wrapper'>
<Building />
</div>
)
}
}

How to get scroll properties in react-simplebar (in stateful function)

I am new with refs in react.js and in the react-simplebar documentation it just shows how to get the scroll ref for a stateless function.
class App extends React.Component {
render() {
console.log(this.refs.scroll) // => Undefined
return (
<Simplebar ref={this.refs.scroll}><h1>scrollable element</h1></Simplebar>
)
}
}
For anyone coming to this at a later date. This is how I managed to set the scrolling position after a few hours of digging around in a function component
const SimpleScrollerComponent = () => {
// Create a reference for the SimpleBar component so we can acces it
const scrollableNodeRef = React.createRef();
const handleScrollDownBtnClicked = () => {
// This is where we set the scroll position
scrollableNodeRef.current.scrollTop = 1200;
};
return (
<div>
{/* We attach the reference to the component */}
<SimpleBar scrollableNodeProps={{ ref: scrollableNodeRef }}>
{/* This is where your code goes inside the scroll box */}
</SimpleBar>
<Button onClick={handleScrollDownBtnClicked}>Scroll to the Bottom</Button>
</div>
);
};
try this
class App extends React.Component {
constructor(props) {
super(props);
this.scrollableNodeRef = React.createRef();
}
onChangeScrollToTop() {
this.scrollableNodeRef.current.scrollTop = 0;
}
render() {
console.log(this.refs.scroll) // => Undefined
return (
<Simplebar scrollableNodeProps={{ ref:this.scrollableNodeRef }}>
<h1>scrollableelement</h1>
</Simplebar>
)
}
}

Browser Back button does not fire componentDidMount() React life cycle method

I have react with redux app the gets a list of order items from Rest API and put on array filled with redux. There is a link in the same list that navigates to the details of an order. When navigating to the details and press the browser back button in order to load the list again I got the following error.
TypeError: fetchedOrders.map is not a function
this is the code inside orders list container
componentDidMount() {
this.props.handleFetchOrders(1);
}
render() {
{ fetchedOrders && fetchedOrders.map(order => {
return (
<OrdersWE
order={order}
onDelete={onDelete}
history = {this.props.history}
key={order.OrderHID}
/>
);
})}
}
const mapDispatchToProps = dispatch => {
return {
handleFetchOrders: (pageNumber) => {
dispatch(fetchOrders(pageNumber));
}
};
};
You missed the return statement from render function, also you have to return a single ReactElement:
class App extends React.Component {
...
render() {
return (
<>
{fetchedOrders &&
fetchedOrders.map(order => {
return (
<OrdersWE
order={order}
onDelete={onDelete}
history={this.props.history}
key={order.OrderHID}
/>
);
})}
</>
);
}
}
Moreover, try to log fetchedOrders, according to the error it may be not an array object.

ReactJS Assigning Value inside a class component method to props

I'm trying to access the index of the selected tab in a React component so as to map it to props as follows:
class AttendanceDetail extends React.Component {
handleSelect(key, props) {
console.log(key)
props.index = key;
}
render(){
const {single_class, courses, attendances} = this.props;
// console.log(this.state);
if(single_class) {
return(
<div className='container content-section'>
// Some irrelevant Code
<Tabs defaultActiveKey={0} onSelect={this.handleSelect} id="uncontrolled-tab-example">
{ courses.map((course, index) => {
return (
<Tab eventKey={index} title={course.course + " year " + course.yearofstudy}>
//Other irrelevant code...
</Tab>
)
})}
</Tabs>
</div>
)
} else {
return (
<div className='container content-section'>
Loading Unit details...
</div>
);
}
}
}
So basically the handleSelect method is what determines the index of the selected tab and logs it to the console. The problem is, I'm tring to map that key (index) to props so as to access it else where but to no avail. Could someone help me out? What am I missing?
You're not supposed to set the component's props, only read. You can use state within the component:
export class Wrapper extends React.Component {
constructor() {
this.state = {
index: 0 //initial state
}
}
handleSelect(index, props) {
this.setState({index})
}
render() {
return (
<span>{this.state.index}</span>
)
}
}
you can read more on the official docs.
if i understood the scenario correctly, you need to log index value of the currently active tab. try using onFocus event handler to get the index value of the currently visible tab and set the state that will be used by handleSelect
constructor(props){
super(props);
this.state = {
index:''
}
}
the handler definition
setIndex = (index) => {
this.setState({index})
}
update handleSelect
handleSelect(index) {
console.log(index)
// call event handler of parent component eg: this.props.getIndex(index);
}
update tabs component handler
<Tabs defaultActiveKey={0} onSelect={() => {this.handleSelect(this.state.index)}} id="uncontrolled-tab-example">
call handler on focus of tab
<Tab
onFocus={() => {this.setIndex(index)}}
eventKey={index}
title={course.course + " year " + course.yearofstudy}>
//Other irrelevant code...
</Tab>

React - Forwarding multiple refs

I have a SideNav component that contains dynamically created links that need to scroll to a corresponding header in an adjacent html table (InfoTable). I've tried multiple different ways of accomplishing this to no avail.
export default class Parent extends Component {
state = {
categories: [],
}
scrollToHeader = (tableRefString) => {
// Function to pass to SideNav to access refs in InfoTable
this[tableRefString].scrollIntoView({ block: 'start' });
}
render() {
return (
<div>
<SideNav
categories={this.state.categories}
scrollToHeader={this.scrollToHeader} />
<InfoTable
categories={this.state.categories} />
</div>
);
}
}
export default class InfoTable extends Component {
render() {
return (
<div>
<table>
<tbody>
{this.props.categories.map(category => (
<>
// Forward the ref through InfoTableHeader to be set on the parent DOM node of each InfoTableHeader
<InfoTableHeader />
{category.inputs.map(input => <InfoTableRow />)}
</>
))}
</tbody>
</table>
</div>
);
}
}
In order to click a link on SideNav and scroll to the corresponding header on InfoTable, I believe that I need to forward refs that are dynamically created on Parent based on names in my categories array and set these refs to the DOM nodes for each header in InfoTable. From there I would pass a function to SideNav that could access the refs in Parent in order to scroll to the header.
How can I go about forwarding multiple refs at once to my InfoTable component?
Is there a cleaner way to accomplish what I'm trying to do? I've looked into React.findDOMNode() but refs seem to be the better option.
I know there is an already accepted answer, and while I find #nicholas-haley's solution acceptable.
I think a better way to go about it would be to use the built-in useImperativeHandle hook.
IMPORTANT: The React Hooks Api is available as of
react#16.8.0 and later
react-native#0.59.0 and later
The React hooks API Docs state:
useImperativeHandle customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with `forwardRef
This note is followed by the following example:
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
Thus, in my opinion, a much cleaner solution would be to delegate the needed refs through the useImperativeHandle hook.
This way there is no need for a special ref syntax, and the component can simply return a specific type of a FatherRef; Example:
// LabelInput.js
function LabelInput(props, ref) {
const labelRef = useRef();
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
get input() {
return inputRef.current;
},
get label() {
return labelRef.current;
},
// ... whatever else one may need
}));
return (
<div>
<label ref={labelRef} ... />
<input ref={inputRef} ... />;
</div>
)
}
LabelInput = forwardRef(LabelInput);
function MyScreen() {
const labelInputRef = useRef();
const onClick = useCallback(
() => {
// labelInputRef.current.focus(); // works
// labelInputRef.current.input.focus(); // works
// ... etc
},
[]
);
return (
...
<LabelInput ref={labelInputRef} ... />
....
)
}
I had a similar situation where I needed multiple refs to be forwarded to the child of my Parent component.
I still have not found an elegant solution, however you might try passing your refs as an object, and destructuring within the forwardRef callback:
// Parent
ref={{
ref1: this.ref1,
ref2: this.ref2
}}
// Child
export default React.forwardRef((props, ref) => {
const { ref1, ref2 } = ref;
return (
<Child1
{...props}
ref={ref1}
/>
<Child2
{...props}
ref={ref2}
/>
);
});
I'm not a big fan of the naming here (I'd prefer ref to be called refs), but this works in case you're in a pinch.
EDIT:
In 2020 I believe #samer-murad's answer is the best solution to this problem.
I actually just picked this up from react-hook-form, but they presented a nice way to share ref and assign multiple refs at once:
<input name="firstName" ref={(e) => {
register(e) // use ref for register function
firstNameRef.current = e // and you can still assign to ref
}} />
I still don't understand what goes on in that ref property, but the following React source code typeof type.render === 'function' + the other answers led me to try passing a function of hoisted refs from parent, and it works!
class Child extends React.Component {
render() {
return (
<div>
<div
ref={this.props.pa}
style={this.props.style}
onClick={async () => {
this.storeAuth(...this.props.storableAuth);
this.props.clearStore();
}}
/>
<div
ref={this.props.fwd}
style={this.props.style}
onClick={async () => {
this.props.onStart();
const res = await this.getUserInfo(verbose, auth, storedAuth);
if (res === "login?") this.props.onPromptToLogin();
if (res) this.props.onFinish(); //res.isAnonymous
}}
/>
</div>
);
}
}
export default React.forwardRef((props, getRefs) => {
const { pa, fwd } = props.getRefs();
return <Child fwd={fwd} pa={pa} {...props} />;
});
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.pa = React.createRef();
this.fwd = React.createRef();
}
render() {
return (
<Child
getRefs={() => {
return {
pa: this.pa,
fwd: this.fwd
};
}}
storableAuth={this.state.storableAuth}//[]
clearAuth={() => this.setState({ storableAuth: null })}
/>
);
}
}
This is not exactly what the author of this question asked, but this title could feet to this question as well: how do I allow developers using my React component to pass a ref if I pass ref internally in my component also (mean, passing multiple refs that will get a ref for this element),
this is the solution i came with:
import { useState, useRef } from "react";
export default function App() {
const [, render] = useState({});
const reRender = () => render({});
const divRef = useRef();
console.log("App", divRef);
return <Component reRender={reRender} passPropsToDiv={{ ref: divRef }} />;
}
const Component = ({ passPropsToDiv, reRender }) => {
const div1Ref = useRef();
const { ref: extraRef = null, ...passPropsToDivNoRef } = passPropsToDiv ?? {};
extraRef.current = div1Ref.current;
console.log("Component", div1Ref);
return (
<div className="App">
<div ref={div1Ref} {...passPropsToDivNoRef}>
i will have a ref
</div>
<button onClick={reRender}>reRender</button>
</div>
);
};
codesandbox: https://codesandbox.io/s/react-use-pass-multiple-refs-legm7p?file=/src/App.js

Resources