Skip to content

Commit 2f8e6b4

Browse files
authored
Merge pull request react-bootstrap#4258 from react-bootstrap/fix/navbar-lifecycle-warning
fix(navbar): resolve lifecycle warning issue
2 parents bbd9630 + ab49a83 commit 2f8e6b4

1 file changed

Lines changed: 79 additions & 92 deletions

File tree

src/Navbar.js

Lines changed: 79 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
import classNames from 'classnames';
2-
import React from 'react';
2+
import React, { useMemo, useCallback } from 'react';
33
import PropTypes from 'prop-types';
44

5-
import { uncontrollable } from 'uncontrollable';
5+
import { useUncontrolled } from 'uncontrollable';
66

77
import createWithBsPrefix from './utils/createWithBsPrefix';
88
import NavbarBrand from './NavbarBrand';
99
import NavbarCollapse from './NavbarCollapse';
1010
import NavbarToggle from './NavbarToggle';
11-
import { createBootstrapComponent } from './ThemeProvider';
11+
import { useBootstrapPrefix } from './ThemeProvider';
1212
import NavbarContext from './NavbarContext';
1313
import SelectableContext from './SelectableContext';
1414

1515
const propTypes = {
1616
/** @default 'navbar' */
17-
bsPrefix: PropTypes.string.isRequired,
17+
bsPrefix: PropTypes.string,
1818

1919
/**
2020
* The general visual variant a the Navbar.
@@ -122,103 +122,90 @@ const defaultProps = {
122122
collapseOnSelect: false,
123123
};
124124

125-
class Navbar extends React.Component {
126-
state = {
127-
navbarContext: {
128-
onToggle: () => this.handleToggle(),
125+
const Navbar = React.forwardRef((props, ref) => {
126+
let {
127+
bsPrefix,
128+
expand,
129+
variant,
130+
bg,
131+
fixed,
132+
sticky,
133+
className,
134+
children,
135+
// Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
136+
as: Component = 'nav',
137+
expanded,
138+
onToggle,
139+
onSelect,
140+
collapseOnSelect,
141+
...controlledProps
142+
} = useUncontrolled(props, {
143+
expanded: 'onToggle',
144+
});
145+
146+
bsPrefix = useBootstrapPrefix(bsPrefix, 'navbar');
147+
148+
const handleCollapse = useCallback(
149+
(...args) => {
150+
if (onSelect) onSelect(...args);
151+
if (collapseOnSelect && expanded) {
152+
onToggle(false);
153+
}
129154
},
130-
};
131-
132-
static getDerivedStateFromProps({ bsPrefix, expanded }, prevState) {
133-
return {
134-
navbarContext: {
135-
...prevState.navbarContext,
136-
bsPrefix,
137-
expanded,
138-
},
139-
};
155+
[onSelect, collapseOnSelect, expanded, onToggle],
156+
);
157+
158+
// will result in some false positives but that seems better
159+
// than false negatives. strict `undefined` check allows explicit
160+
// "nulling" of the role if the user really doesn't want one
161+
if (controlledProps.role === undefined && Component !== 'nav') {
162+
controlledProps.role = 'navigation';
140163
}
164+
let expandClass = `${bsPrefix}-expand`;
165+
if (typeof expand === 'string') expandClass = `${expandClass}-${expand}`;
141166

142-
handleCollapse = (...args) => {
143-
const { onToggle, expanded, collapseOnSelect, onSelect } = this.props;
144-
145-
if (onSelect) onSelect(...args);
146-
if (collapseOnSelect && expanded) {
147-
onToggle(false);
148-
}
149-
};
150-
151-
handleToggle = () => {
152-
const { onToggle, expanded } = this.props;
153-
154-
onToggle(!expanded);
155-
};
156-
157-
render() {
158-
const {
167+
const navbarContext = useMemo(
168+
() => ({
169+
onToggle: () => onToggle(!expanded),
159170
bsPrefix,
160-
expand,
161-
variant,
162-
bg,
163-
fixed,
164-
sticky,
165-
className,
166-
children,
167-
// Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
168-
as: Component = 'nav',
169-
expanded: _1,
170-
onToggle: _2,
171-
onSelect: _3,
172-
collapseOnSelect: _4,
173-
...props
174-
} = this.props;
175-
176-
// will result in some false positives but that seems better
177-
// than false negatives. strict `undefined` check allows explicit
178-
// "nulling" of the role if the user really doesn't want one
179-
if (props.role === undefined && Component !== 'nav') {
180-
props.role = 'navigation';
181-
}
182-
let expandClass = `${bsPrefix}-expand`;
183-
if (typeof expand === 'string') expandClass = `${expandClass}-${expand}`;
184-
185-
return (
186-
<NavbarContext.Provider value={this.state.navbarContext}>
187-
<SelectableContext.Provider value={this.handleCollapse}>
188-
<Component
189-
{...props}
190-
className={classNames(
191-
className,
192-
bsPrefix,
193-
expand && expandClass,
194-
variant && `${bsPrefix}-${variant}`,
195-
bg && `bg-${bg}`,
196-
sticky && `sticky-${sticky}`,
197-
fixed && `fixed-${fixed}`,
198-
)}
199-
>
200-
{children}
201-
</Component>
202-
</SelectableContext.Provider>
203-
</NavbarContext.Provider>
204-
);
205-
}
206-
}
171+
expanded,
172+
}),
173+
[bsPrefix, expanded, onToggle],
174+
);
175+
176+
return (
177+
<NavbarContext.Provider value={navbarContext}>
178+
<SelectableContext.Provider value={handleCollapse}>
179+
<Component
180+
ref={ref}
181+
{...controlledProps}
182+
className={classNames(
183+
className,
184+
bsPrefix,
185+
expand && expandClass,
186+
variant && `${bsPrefix}-${variant}`,
187+
bg && `bg-${bg}`,
188+
sticky && `sticky-${sticky}`,
189+
fixed && `fixed-${fixed}`,
190+
)}
191+
>
192+
{children}
193+
</Component>
194+
</SelectableContext.Provider>
195+
</NavbarContext.Provider>
196+
);
197+
});
207198

208199
Navbar.propTypes = propTypes;
209200
Navbar.defaultProps = defaultProps;
201+
Navbar.displayName = 'Navbar';
210202

211-
const DecoratedNavbar = createBootstrapComponent(
212-
uncontrollable(Navbar, { expanded: 'onToggle' }),
213-
'navbar',
214-
);
215-
216-
DecoratedNavbar.Brand = NavbarBrand;
217-
DecoratedNavbar.Toggle = NavbarToggle;
218-
DecoratedNavbar.Collapse = NavbarCollapse;
203+
Navbar.Brand = NavbarBrand;
204+
Navbar.Toggle = NavbarToggle;
205+
Navbar.Collapse = NavbarCollapse;
219206

220-
DecoratedNavbar.Text = createWithBsPrefix('navbar-text', {
207+
Navbar.Text = createWithBsPrefix('navbar-text', {
221208
Component: 'span',
222209
});
223210

224-
export default DecoratedNavbar;
211+
export default Navbar;

0 commit comments

Comments
 (0)