I'm trying to build a Dashboard with React Js and I would like to know how you could display multiple components, but as widgets, which mean you need to be able to add them in any order and in any quantity. My problem is that I can't find a way to render a map of components.
The user may be able to add a widget to his dashboard by clicking a button, or remove them the same way, so I won't know how many of what components will be rendered
I would like to do something like, when the user clicks on a widget, it adds it to the dashboard, but I don't know how to do that.
I've tried to store components in a map, then with a forEach loop display them all by returning a div containing the component:
import Weather from '...'
import Currency from '...'
import News from '...'
const map = [Weather, Currency, News]
const runAll = () => {
map.forEach((fcn) => {
let runner = fcn
runner()
})
}
runAll()
I've searched many stack and other forums questions, without finding what I needed
Do you guys have an idea of what I could do to solve this ?
CodePudding user response:
So you need to be able to easily render 2 things:
- a list of widgets that the user can click and add in the dashboard
- the actual dashboard. All selected widgets in a list (with a remove capability)
Let's first figure out what our state
should be that also feeds the components 1 and 2.
- For the 1st one we need the full list of available widgets. Since this is static (we have 3 widgets available) this can be expresses through a static mapping (a simple javascript object) declared once.
- For the 2nd one we need an array of the user selected widgets. That's the dynamic part. We need to be able to set the initial widgets shown and have the capability to add and remove widgets from this list, allowing the same widget appearing more that once.
Static widget mapping
This should be a mapping between an identifier and the react widget component and should look like this:
import News from "./News";
import Weather from "./Weather";
import Currency from "./Currency";
const widgetsMapping = {
news: News,
weather: Weather,
currency: Currency
};
Widgets state
This is an array of widget identifiers (the keys from the static mapping) that the user wants in the dashboard. Also we need add and remove methods. Using useState
we can write this like below:
const [widgets, setWidgets] = useState(["weather", "news"]);
const addWidget = (widget) => {
setWidgets([...widgets, widget]);
};
const removeWidget = (index) => {
const updated = [...widgets];
updated.splice(index, 1);
setWidgets(updated);
};
Rendering
Dashboard
Then we can render the dashboard by iterating our widget
state array:
{widgets.map((widget, index) => {
const Widget = widgetsMapping[widget];
return (
<Widget
key={`${widget}${index}`}
removeWidget={() => removeWidget(index)}
/>
)
})}
removeWidget
prop can be used to let a widget remove itself when sth is clicked.
List of available widgets
Here we will iterate through all available widgets from our static mapping and render all of them with the add functionality bound to them.
{Object.keys(widgetsMapping).map((widget) => (
<button key={widget} onClick={() => addWidget(widget)}>
{widget}
</button>
))}
You can find a full working example in this code sandbox. Some assumptions were made about how you want to add and remove widgets but the main idea remains the same.
CodePudding user response:
Keep a state (array) that holds widgets added by user. Define constants for widgets and save these constants to your persistance storage.
const widgets = {weather : 1, news: 2} save these values to database as json with properties configured by user if needed, and then retrieve this json and render components based on it
sample JSON structure to save - [{type: 1, prop1: "val"},{type: 2, prop1: "val"}]
const renderWidgets = (array) => {
const widgets = [];
array.foreach((widget) => {
switch(widget) {
case widgets.weather:
widgets.push(<Weather ...props/>);
break;
.
.
.
etc
}
});
return widgets;
}