Given the following code:
export const AdmButton = ({icon,title,iconSize}) =>{
return({icon} {title})
buttondata.map((el, index) => {
<AdmButton title={el.title} icon={el.icon} iconSize="3em"/>
})
export const buttondata=[{
title:'calc',
icon: <ImIcons.ImCalculator size="2em"/>,
},*/.../*
]
The AdmButton is by iteration created as many times as objects in buttondata. The object contains as icon the component from react-icons, in the example ImIcons.ImCalculator is being used. Now, this component has a property called size which I'd like to change depending the place the button is shown, but I don't find a way to refer to size (or another property) when the component is being called via object property. What's the right way to modify or overwrite a Property of a given component iterated this way?
Instead of being a hard-coded component, the icon property in your object can be a function which generates a component. And that function can accept an argument:
icon: (size) => <ImIcons.ImCalculator size={size} />
Then in your AdmButton component you can invoke that function with the parameter:
return({icon(iconSize)} {title})
So each iteration when mapping will pass the parameter along and instantiate the icon on the fly.
Side note: Your call to .map() is missing the return in the callback, and as such currently won't render anything. Either include a return or remove the curly braces to make use of an implicit return.
Related
I am trying to implement the drag-n-drop feature on a nested array of inputs. I am using react-beautiful-dnd and react-hook-form libraries: useFieldArray hook to manage the form and useController hook for controlled input.
The problem I am facing is when i try to reorder any child input, change happens in the last item only. here is a sandbox.
To reproduce the issue, you need to add at least 2 top-level inputs along with 2 nested inputs for each of those. Next try to reorder the nested inputs of the 1st top-level item. As a result, child inputs of the 2nd item would change their positions, while the initially dragged inputs would revert back to their original position.
I tried to pass ref to nested inputs and used useImperativeHandle hook to expose move method of useFieldArray hook from child component to reorder. But everytime only the last child gets reordered
SOLVED
I was using the same ref for my nested field array to store reorder child function therefore when appending a new nested child field array, it was overriding the reorder function and only moving items belonging to the last nested field array.
To solve this, I had to pass a function instead of ref to the nested fields which creates different ref for each
function Parent () {
const childrenRef = useRef({});
const setReorder = useCallback(
(index, reorderCallback) => {
childrenRef.current[index] = reorderCallback;
},
[childrenRef]
);
...
return (
<Child
setReorder={setReorder}
/>
)
}
function Child ({ setReorder, nestIndex }) {
useEffect(() => {
setReorder(`child-${nestIndex}`, (from, to) => {
move(from, to);
});
}, [nestIndex, setReorder, move]);
}
Here is the working sandbox
I'm using Open Layer to render map functionality in a react application. I used the simple code snippets to display the map in the application. Unfortunately the application renders the map twice as below.
Here is the code snippet I used to display the map:
export default function MapView() {
const view = new View({
center: [0, 0],
zoom: 2,
});
useEffect(() => {
new Map({
layers: [
new TileLayer({source: new OSM()}),
],
view: view,
target:'map',
})
},[])
return (
<>
<div id="map" style={{width:'100%',height:'400px'}}></div>
</>
)
}
And the parent component is as below
function App() {
return (
<div>
<MapView />
</div>
);
}
Your component may be mounted multiple times; useEffect with an empty deps array runs each time the component is mounted, but instantiating View/Map appears to mutate the DOM directly and inject the map. If you look at my example, the message "I'm mounting!" is printed to the console twice. Because useEffect is running once per mount, you're instantiating multiple Map objects and attaching them to the element with id map. Instead, you should consider using a ref to hold your Map reference so that only one map object is instantiated per instance of your component.
Additionally, rather than referencing an ID by string in your map mounting, you should instead use a ref for the component's rendered node, instead, and pass that reference to target (which does appear to accept an HTMLElement node directly).
See this sandbox link for an example.
This ref handle also gets you a persistent reference to the map object, which you can then use to perform imperative operations on the map in response to prop changes (see my example zoom-sensitive useEffect).
I'm building a front application with reactjs and material-ui. I have Form that call Field components.
To have more beautiful forms i use Tabs. So i follow the material-ui doc that use TabPanel function to wrap tab content. But i made a mistake, i put function inside my component Test
export default function Test(props) {
function TabPanel(props) {
const { children, value, index, ...other } = props;
return value === index && <Box p={3}>{children}</Box>;
}
}
Instead of doing this :
function TabPanel(props) {
const { children, value, index, ...other } = props;
return value === index && <Box p={3}>{children}</Box>;
}
export default function Test(props) {
}
With the first version, i lost my focus on my input field after each change. On the second version everything was ok.
Could you tell me why it's different.
Thanks and regards
The first scenario is as this:
Because you have declared your TabPanel renderer function with the keyword function, it won't be bound to the scope of your functional component Test, in order to do this, you'll have to do a lot more job -if you chose the purest JS way-, bounding a function to it's Direct Parent Scope(Test) makes it statically preserve the first copy of it(TabPanel) during the life time of the parent.
In other words, bounding your TabPanel function to the local scope of the Test function will preserve the same copy of the TabPanel function.
While, if it's not bound, a new function TabPanel will be created and used each time a render happens in the Test component, thus, it'll entirely re-render your input element and then lose it's focus.
to make the first scenario work you can use the ES6 Arrow Function, as this:
export default function Test(props) {
const TabPanel = (props) => {
const { children, value, index, ...other } = props;
return value === index && <Box p={3}>{children}</Box>;
}
}
Why would that work? but not the function keyword way? because ES6 Arrow Functions Auto bind them self to their Lexical Scope -The scope when they where created(Test() Scope)-, remember, they do it automatically, there is a bunch of stuff that happen under the hood in order for that to happen..
While the second way works just fine because JS will keep a copy of your function in the global scope, in our case window is the global scope, it'll be something like window.TabPanel, and it'll be preserved the first time JS goes into this file, so, extracting it out of the Test function into the global scope will protect it from re-creating itself over and over again whenever a re-render occurs..
I hope I was able to deliver the idea..
If you are willing to understand how binding happens, you'll need to go through the prototype nature of JS.
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 figured I could do this, but I am getting this error:
TypeError: child.constructor.ConvenienceConstructor is not a function
I have a component in a page, ala:
// this content is in an html page. My component reads in this child, but I can't seem to modify any part of it.. Just diplay it.
<MyComponent prop1="somevalue">
<div className="myclass1"> some child content that is dynamic </div>
</MyComponent>
Now, in my component since that inner child(ren) is dynamic, I need to change that class depending on some condition. But I can't. I get that error I noted above.
I tried this:
var childContent = React.Children.map(this.props.children,
function(child) {
return React.cloneWithProps(child,
{ className: 'myNEWClass' } );
});
I tried cloneElement too, that didn't work either.
Doesn't work. I tried accessing the child directly, ala:
child._store.props.className // but can't seem to change it, seems immutable.
So, how can I change that class up?
thanks,
Props are supposed to be immutable, so instead of passing className as a prop, you make the parent pass in a prop named defaultClass. In your map method, you can add an extra prop, say, overrideClass. Finally, in the render() method of the actual child component, you need some logic to set the className to either overrideClass, or defaultClass. In this way, you don't have to mutate props.