Skip to content

Commit fba00d6

Browse files
authored
partially fixed Rect complex example (#2151)
React 2 grid demo * fix #2094 * now works dragging between grids and staying in sync. Still not recommended way...
1 parent 27d0fe2 commit fba00d6

File tree

3 files changed

+74
-48
lines changed

3 files changed

+74
-48
lines changed

demo/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ <h1>Demos</h1>
2020
<li><a href="nested_advanced.html">Nested Advanced grids</a></li>
2121
<li><a href="offset.html">Offset parent</a></li>
2222
<li><a href="react-hooks.html">ReactJS (Hooks)</a></li>
23-
<li><a href="react-hooks-controlled-multiple.html">ReactJS (Hooks), mulitple, controlled</a></li>
23+
<li><a href="react-hooks-controlled-multiple.html">ReactJS (Hooks), mulitple, controlled (NOT RECOMMENDED - BROKEN)</a></li>
2424
<li><a href="react.html">ReactJS</a></li>
2525
<li><a href="responsive.html">Responsive</a></li>
2626
<li><a href="right-to-left(rtl).html">Right-To-Left (RTL)</a></li>

demo/react-hooks-controlled-multiple.html

Lines changed: 70 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<title>Gridstack.js React integration example</title>
88
<link rel="stylesheet" href="demo.css" />
9+
<link rel="stylesheet" href="../dist/gridstack-extra.css"/>
910
<script src="../dist/gridstack-all.js"></script>
1011

11-
<!-- Scripts to use react inside html -->
12-
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
13-
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
14-
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
12+
<!-- Scripts to use react inside html - DEVELOPMENT FILES -->
13+
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
14+
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
15+
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.js"></script>
1516
</head>
1617

1718
<body>
@@ -22,8 +23,12 @@ <h2>Controlled stack</h2>
2223
</body>
2324

2425
<script type="text/babel">
26+
/***********************************************************************************************************/
27+
/********************************* NOT IDEAL - see comments below line 111) ********************************/
28+
/***********************************************************************************************************/
29+
2530
const { useState, useEffect, useLayoutEffect, createRef, useRef } = React
26-
const Item = ({ id }) => <div>I am item: {id}</div>
31+
const Item = ({ id }) => <div>{id}</div>
2732

2833
//
2934
// Controlled example
@@ -40,45 +45,57 @@ <h2>Controlled stack</h2>
4045
}
4146

4247
useLayoutEffect(() => {
43-
gridRef.current =
44-
// gridRef.current ||
45-
GridStack.init(
48+
if (!gridRef.current) {
49+
// no need to init twice (would will return same grid) or register dup events
50+
const grid = gridRef.current = GridStack.init(
4651
{
4752
float: true,
4853
acceptWidgets: true,
54+
disableOneColumnMode: true, // side-by-side and fever columns to fit smaller screens
55+
column: 6,
56+
minRow: 1,
4957
},
5058
`.controlled-${id}`
5159
)
52-
const grid = gridRef.current
53-
54-
grid.on('removed', (e, el) => {
55-
const elId = Array.isArray(el) ? el[el.length - 1].id : el.id;
56-
removeItem(elId.split(':')[1])
57-
})
58-
59-
grid.on('dropped', (e, p, n) => {
60-
console.log('dropped', p, n)
61-
// Remove "placeholder" item
62-
// Gridstack creates a DOM element for dropped items, those need to be removed
63-
// as they are rendered by React
64-
grid.getGridItems().forEach((item) => {
65-
const ids = item.getAttribute('gs-id').split(':')
66-
if (ids[0] !== id) {
67-
grid.removeWidget(item, true, false);
68-
}
69-
});
70-
addItem({ id: n.id.split(':')[1], x: n.x, y: n.y, w: n.w, h: n.h })
71-
})
60+
.on('added', (ev, gsItems) => {
61+
if (grid._ignoreCB) return;
62+
// remove the new element as React will re-create it again (dup) once we add to the list or we get 2 of them with same ids but different DOM el!
63+
// TODO: this is really not ideal - we shouldn't mix React templating with GS making it own edits as those get out of sync! see comment below @111.
64+
gsItems.forEach(n => {
65+
grid.removeWidget(n.el, true, false); // true=remove DOM, false=don't call use back!
66+
// can't pass n directly even though similar structs as it has n.el.gridstackNode which gives JSON error for circular write.
67+
addItem({id:n.id, x:n.x, y:n.y, w:n.w, h:n.h});
68+
});
69+
})
70+
.on('removed', (ev, gsItems) => {
71+
if (grid._ignoreCB) return;
72+
gsItems.forEach(n => removeItem(n.id));
73+
})
74+
75+
} else {
76+
//
77+
// update existing grid layout, which is optimized to updates only diffs (will add new/delete items for examples)
78+
//
79+
const grid = gridRef.current;
80+
const layout = [];
81+
items.forEach((a) => layout.push(
82+
refs.current[a.id].current.gridstackNode || {...a, el: refs.current[a.id].current}
83+
));
84+
grid._ignoreCB = true; // hack: ignore added/removed since we're the one doing the update
85+
grid.load(layout);
86+
delete grid._ignoreCB;
87+
}
7288

73-
grid.batchUpdate()
74-
items.forEach((a) => {
75-
// remove existing widgets
76-
if (refs.current[a.id] && refs.current[a.id].current) {
77-
grid.removeWidget(refs.current[a.id].current, false, false)
78-
}
79-
grid.makeWidget(refs.current[a.id].current)
80-
})
81-
grid.batchUpdate(false)
89+
// NOTE: old code is incorrect as it re-does the GS binding, but dragged item is left behind so you get dup DOM elements with same ids
90+
// grid.batchUpdate()
91+
// items.forEach((a) => {
92+
// // remove existing widgets
93+
// if (refs.current[a.id] && refs.current[a.id].current) {
94+
// grid.removeWidget(refs.current[a.id].current, false, false)
95+
// }
96+
// grid.makeWidget(refs.current[a.id].current)
97+
// })
98+
// grid.batchUpdate(false)
8299

83100
}, [items])
84101

@@ -91,12 +108,21 @@ <h2>Controlled stack</h2>
91108
})
92109

93110
return (
94-
<div style={{ width: '100%' }}>
111+
// ********************
112+
// NOTE: constructing DOM grid items in template when gridstack is also allowed editing (dragging between grids, or adding/removing from say a toolbar)
113+
// is NOT A GOOD IDEA as you end up fighting between gridstack users' edits and your template items structure which are not in sync.
114+
// At best, you end up re-creating widgets DOM (from React template) and all their content & state after a widget was inserted/re-parented by the user.
115+
// a MUCH better way is to let GS create React components using it's API/user interactions, with only initial load() of a stored layout.
116+
// see the upcoming Angular component wrapper that does that instead (lib author uses Angular). TBD creating React equivalent...
117+
//
118+
// Also templating forces you to spell out the 12+ attributes GS supports (only x,w,w,h done below) instead of passing a option structure that supports everything
119+
// is not robust as things get added, and pollutes the DOM attr for default/missing entries, vs optimized code in GS.
120+
// ********************
121+
<div style={{ width: '100%', marginRight: '10px' }}>
95122
<div className={`grid-stack controlled-${id}`}>
96123
{items.map((item, i) => {
97-
// console.log('render', id, item.id)
98124
return (
99-
<div ref={refs.current[item.id]} key={`${id}-${item.id}`} className={'grid-stack-item'} gs-id={`${id}:${item.id}`} gs-w={item.w} gs-h={item.h} gs-x={item.x} gs-y={item.y}>
125+
<div ref={refs.current[item.id]} key={`${id}-${item.id}`} className={'grid-stack-item'} gs-id={item.id} gs-w={item.w} gs-h={item.h} gs-x={item.x} gs-y={item.y}>
100126
<div className="grid-stack-item-content">
101127
<Item {...item} />
102128
</div>
@@ -122,10 +148,10 @@ <h2>Controlled stack</h2>
122148
id='gs1'
123149
items={items1}
124150
addItem={(item) => {
125-
setItems1([...items1, item])
151+
setItems1(items => [...items, item])
126152
}}
127153
removeItem={(id) => {
128-
setItems1(items1.filter(i => i.id !== id))
154+
setItems1(items => items.filter(i => i.id !== id))
129155
}}
130156
/>
131157
</div >
@@ -134,10 +160,10 @@ <h2>Controlled stack</h2>
134160
id='gs2'
135161
items={items2}
136162
addItem={(item) => {
137-
setItems2([...items2, item])
163+
setItems2(items => [...items, item])
138164
}}
139165
removeItem={(id) => {
140-
setItems2(items2.filter(i => i.id !== id))
166+
setItems2(items => items.filter(i => i.id !== id))
141167
}}
142168
/>
143169
</div>

demo/react-hooks.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818
<div>
1919
<h1>Using GridStack.js with React hooks</h1>
2020
<p>
21-
As with any virtual DOM based framework, you need to check if Reacthas rendered the DOM (or any updates to it)
21+
As with any virtual DOM based framework, you need to check if React has rendered the DOM (or any updates to it)
2222
<strong>before</strong> you initialize GridStack or call its methods. This example shows how to make rendered
2323
components widgets:
2424
</p>
2525
<ol>
2626
<li>Render items, each with a reference</li>
2727
<li>Convert each rendered item to a widget using the reference and the <a
2828
href="https://github.com/gridstack/gridstack.js/tree/master/doc#makewidgetel">
29-
makeWidget</a> function</li>
29+
makeWidget()</a> function</li>
3030
</ol>
3131
</div>
3232
<div>
@@ -41,7 +41,7 @@ <h2>Uncontrolled stack</h2>
4141

4242
<script type="text/babel">
4343
const { useState, useEffect, createRef, useRef } = React
44-
const Item = ({ id }) => <div>I am item: {id}</div>
44+
const Item = ({ id }) => <div>{id}</div>
4545

4646
//
4747
// Controlled example

0 commit comments

Comments
 (0)