Imperative version of next/router not working with Semantic-UI-React? - javascript

According to the Next.js docs
You can also do client-side page transitions using next/router:
import Router from 'next/router'
function ReadMore() {
return (
<div>
Click <span onClick={() => Router.push('/about')}>here</span> to read more
</div>
)
}
export default ReadMore
I am essentially extrapolating that to my own example with Sematic-UI-React:
This my current behavior which is obviously is not desired.
You can see at some point the <Link/> component or perhaps its the <Menu.Item/> falls out of sync.
This is how the <Menu.Item/> works as per their docs.
You can see how snappy it's behaving, but that's with the <Link/> tag commented out...
This is the greater HOC.
var comparator;
const GenericIsUserLoggedInLink = React.memo(({ isHomeButton, isLoggedIn, logOutUser, route, anchorText, mobile, name, active, handleItemClick }) => {
comparator = (prevProps, nextProps) => {
if (prevProps.isHomeButton !== nextProps.setProps.isHomeButton) {
return true;
}
if (prevProps.isLoggedIn !== nextProps.setProps.isLoggedIn) {
return true;
}
if (prevProps.mobile !== nextProps.setProps.mobile) {
return true;
}
if (prevProps.name !== nextProps.setProps.name) {
return true;
}
if (prevProps.active !== nextProps.setProps.active) {
return true;
}
return false;
}
function currentNav(route, name, active, handleItemClick) {
// console.log("handleItemClick ", handleItemClick);
// console.log("active ", active);
// console.log("name ", name);
/* This is where I extrapolated their imperative version */
function MyLink({children, route}) {
console.log(`route ${route}`)
return (
<>
<span onClick={() => Router.push(route)}>{children}</span>
</>
)
}
return (
<MyLink route={route}>
<Menu.Item
to={route}
key={name}
name={name}
active={active == name}
onClick={(e) => {
handleItemClick(e, { name });
}
}
>
</Menu.Item>
</MyLink>
);
}
if (isHomeButton) {
return currentNav(route, name, active, handleItemClick)
}
if (isLoggedIn) {
if (anchorText === undefined) {
return <Link href="/"><a onClick={() => logOutUser()}>Log out!</a></Link>
}
else if (mobile) {
return currentNav(route, name, active, handleItemClick)
}
else if (!(mobile)) {
return currentNav(route, name, active, handleItemClick)
}
else if (anchorText) {
return <Link href={route}><a>{anchorText}</a></Link>
}
} else {
if (route === "/login") {
return <Link href="/login"><a>Log in!</a></Link>
}
return null
}
}, comparator);
Any help would be appreciated!

Related

Add Class On Page Reload In Next JS

I'm kind of new w/ react and nextjs. How can insert a script in a component to add class in when the page reload? It seems like the code below don't work because the page is not yet rendered when I add the class for the body tag.
const ModeToggler = (props: Props) => {
// ** Props
const { settings, saveSettings } = props
const handleModeChange = (mode: PaletteMode) => {
saveSettings({ ...settings, mode })
}
const handleModeToggle = () => {
if (settings.mode === 'light') {
handleModeChange('dark');
document.body.classList.add('mode-dark');
} else {
handleModeChange('light');
document.body.classList.remove('mode-dark');
}
}
// This will not work because the page is not rendered yet right?
if (settings.mode === 'light') {
document.body.classList.add('mode-dark');
} else {
document.body.classList.remove('mode-dark');
}
return (
<IconButton color='inherit' aria-haspopup='true' onClick={handleModeToggle}>
{settings.mode === 'dark' ? <WeatherSunny /> : <WeatherNight />}
</IconButton>
)
}
export default ModeToggler
In your case, since the class is dependent on the prop settings
I would suggest you use a useEffect with a dependency of that prop. So not only will it retrieve the value and apply the style on render, but also re-render/apply style each time the prop changes.
const ModeToggler = (props: Props) => {
// ** Props
const { settings, saveSettings } = props
const handleModeChange = (mode: PaletteMode) => {
saveSettings({ ...settings, mode })
}
const handleModeToggle = () => {
if (settings.mode === 'light') {
handleModeChange('dark');
document.body.classList.add('mode-dark');
} else {
handleModeChange('light');
document.body.classList.remove('mode-dark');
}
}
useEffect(() => {
if (settings.mode === 'light') {
document.body.classList.add('mode-dark');
} else {
document.body.classList.remove('mode-dark');
}
}, [settings.mode])
return (
<IconButton color='inherit' aria-haspopup='true' onClick={handleModeToggle}>
{settings.mode === 'dark' ? <WeatherSunny /> : <WeatherNight />}
</IconButton>
)
}
export default ModeToggler
you can detect if your page is reload or not using window.performance
for more info https://developer.mozilla.org/en-US/docs/Web/API/Performance/getEntriesByType
useEffect(()=>{
let entries = window.performance.getEntriesByType("navigation");
if(entries[0]=="reload"){
// add class to an element;
}
},[])

display child accordion only when parent accordion is opened

I am developing an accordion which is nested in character. I mean, an accordion can have its child accordion as well. Right now, a simple accordion has been build up. The toggling part works either. However, there is a problem. Child accordion is shown without opening the parent accordion. How can i show child accordion only when parent accordion is clicked and hide it when clicked again?
Here is what i have done
class Accordion extends React.Component {
constructor(props) {
super(props);
let state = { activeSections: {} };
React.Children.toArray(props.children).forEach(child => {
if (child) {
state.activeSections[child.props.name] = !!child.props.defaultOpen;
}
});
this.state = state;
}
get isControlled() {
return typeof this.props.onToggle === "function";
}
expandAll = () => {
if (this.isControlled) {
this.props.expandAll();
} else {
let { activeSections } = this.state;
Object.keys(activeSections).forEach(k => (activeSections[k] = true));
this.setState({ activeSections });
}
};
collapseAll = () => {
if (this.isControlled) {
this.props.collapseAll();
} else {
let { activeSections } = this.state;
Object.keys(activeSections).forEach(k => (activeSections[k] = false));
this.setState({ activeSections });
}
};
onToggle = name => {
if (this.isControlled) {
this.props.onToggle(name);
} else {
let activeSections = this.state.activeSections;
this.setState({
activeSections: { ...activeSections, [name]: !activeSections[name] }
});
}
};
componentWillReceiveProps(nextProps) {
let { activeSections } = this.state;
React.Children.toArray(nextProps.children)
.filter(c => c)
.forEach(child => {
if (activeSections[child.props.name] == null) {
activeSections[child.props.name] = !!child.props.defaultOpen;
}
});
this.setState({ activeSections });
}
render() {
let { activeSections } = this.state;
let children = React.Children.toArray(this.props.children);
// if (!children.find(c => c && c.type === AccordionHeader)) {
// children = [<AccordionHeader />, ...children];
// }
console.log("children", children);
return (
<div>
{children.map(child => {
if (!child) {
return child;
} else if (child.type === AccordionItem) {
return React.cloneElement(child, {
expanded: this.isControlled
? child.props.expanded
: activeSections[child.props.name],
onToggle: this.onToggle
});
} else {
return child;
}
})}
</div>
);
}
}
export default Accordion;
class AccordionItem extends React.Component {
render() {
let {
expanded,
caption,
onToggle,
name,
children,
render,
...rest
} = this.props;
return render ? (
render({ onToggle: onToggle.bind(null, name), expanded })
) : (
<styled.AccordionItem style={{ margin: 10 }}>
<styled.AccordionHeader
onClick={() => onToggle(name)}
active={expanded}
>
{caption}
</styled.AccordionHeader>
<styled.AccordionBody active={rest.defaultOpen || expanded}>
{children && (
<styled.AccordionBodyContent>
{children}
</styled.AccordionBodyContent>
)}
</styled.AccordionBody>
</styled.AccordionItem>
);
}
}
export default AccordionItem;
I have create a working example in the sandbox and here it is
https://codesandbox.io/s/z25j8q2v4m

React Native rendering same route

I want to change routes on button press in React Native. In my SplashContainer component this is the method I'm running:
handleToSignUp = () => {
console.log("Running handleToSignUp")
this.props.navigator.push({
signUpForm: true
});
}
My Navigator component looks like this
export default class NimbusNavigator extends Component {
static propTypes = {
isAuthed: PropTypes.bool.isRequired
}
renderScene = (route, navigator) => {
console.log(route);
console.log(navigator);
// Keeps track of whether user is Authed or not.
if (this.props.isAuthed === false && route !== 'signUpForm') {
return <SplashContainer navigator={navigator}/>
} else if (route === 'signUpForm') {
return <SignUpForm navigator={navigator} />
}
return <FooterTabsContainer navigator={navigator} />
}
configureScene = (route) => {
return Navigator.SceneConfigs.FloatFromRight
}
render () {
return (
<Navigator
configureScene={this.configureScene}
renderScene={this.renderScene}
/>
)
}
}
If route is not equal to 'signUpForm' shouldn't code skip to the else if statement and render <SignUpForm/> component?
Thanks!
There are a few ways to get this working, but the main thing to remember is that everything passed to navigator.push({ // properties of the route object }) become properties of the route object.
For instance, if you keep your handleSignUp method the way it is, you would need to rewrite your renderScene method like so:
renderScene = (route, navigator) => {
if (this.props.isAuthed === false && !route.signUpForm) {
return <SplashContainer navigator={navigator}/>
} else if (route.signUpForm) {
return <SignUpForm navigator={navigator} />
}
return <FooterTabsContainer navigator={navigator} />
}
You could also rewrite your handleSignUp method like this:
handleToSignUp = () => {
this.props.navigator.push({
title: 'handleSignUpForm'
});
}
And renderScene like this:
renderScene = (route, navigator) => {
if (this.props.isAuthed === false && route.title !== 'signUpForm') {
return <SplashContainer navigator={navigator}/>
} else if (route.title === 'signUpForm') {
return <SignUpForm navigator={navigator} />
}
return <FooterTabsContainer navigator={navigator} />
}

ReactJS Onclick not working for generated elements

I am generating a list of elements with:
LeftPanel.js
if (this.state.trader_or_team == 'trader') {
itemList = users.map(u => <User key={u.id} user={u}
selected={this.props.selected_trader && this.props.selected_trader.id == u.id}
onClick={this.props.handleTraderSelection.bind(this, u)}
/>);
} else {
itemList = teams.map(t => <Team key={t.id} team={t}
selected={this.props.selected_team && this.props.selected_team.id == t.id}
onClick={this.props.handleTeamSelection.bind(this, t)}
/>)
}
handleTeamSelection/handleTraderSelection are in the parent component:
TargetManager.js
handleTraderSelection(selected_trader) {
console.log('test')
this.setState({
selected_trader
});
}
handleTeamSelection(selected_team) {
this.setState({
selected_team
});
}
They are passed down as props:
<LeftPanel
handleTraderSelection={::this.handleTraderSelection}
handleTeamSelection={::this.handleTeamSelection}
/>
And rendered:
LeftPanel.js
return(
<div className="item-container">
{itemList}
</div>
)
When I click on any of the elements nothing happen at all. What is going wrong?
It appears to break when I bind something to it, in the render method of LeftPanel.js:
render() {
this.props.handleTraderSelection()
...
Works,
render() {
this.props.handleTraderSelection.bind(this)
...
Does not work.
Try binding the handleTraderSelection and handleTeamSelection function like
handleTraderSelection = (selected_trader) => {
console.log('test')
this.setState({
selected_trader
});
}
handleTeamSelection = (selected_team) => {
this.setState({
selected_team
});
}
I have been following the conventional method to call the parent function like
handleTraderSelection = (value) => {
this.props.handleTraderSelection(value);
}
handleTeamSelection= (value) => {
this.props.handleTeamSelection(value);
}
if (this.state.trader_or_team == 'trader') {
itemList = users.map(u => <User key={u.id} user={u}
selected={this.props.selected_trader && this.props.selected_trader.id == u.id}
onClick={this.handleTraderSelection.bind(this, u)}
/>);
} else {
itemList = teams.map(t => <Team key={t.id} team={t}
selected={this.props.selected_team && this.props.selected_team.id == t.id}
onClick={this.handleTeamSelection.bind(this, t)}
/>)
}
and it works for me well.
I was missing my onClick for my elements...:
export default class User extends Component {
render() {
return (
<div onClick={this.props.onClick}>
...

Prefer pure function than React Component?

I'm working on a HTMl5 video player for a French company. We use React and Redux to build the UI, and it works very well, it's very pleasant to code ! We currently use eslint-plugin-react to check React code style. Since last versions, the linter recommands to use pure functions instead of React Components (view the rule) but it raises some debates in my team.
We are already using pure function for very small components that render always the same things (for given props). No problems with that. But for bigger components, in my opinion, pure functions seem to make the code less elegant (and less uniform compared to other components).
This is an example of one of our components that should be changed :
const ControlBar = ({ actions, core, root }) => {
const onFullscreenScreen = (isFullscreen) => {
const playerRoot = root;
if (isFullscreen && !screenfull.isFullscreen) {
screenfull.request(playerRoot);
} else {
screenfull.exit();
}
};
​
const renderIconButton = (glyph, action, label = false) => {
let content = (
<Button modifier="icon" clickCallback={ action }>
<Icon glyph={ glyph } />
</Button>
);
if (label) {
content = <TooltipOrigin content={label}>{content}</TooltipOrigin>;
}
return content;
};
​
const renderPlayButton = () => {
const { play, pause } = actions;
const { playerState } = core;
if (playerState === CoreStates.PAUSED) {
return renderIconButton(playGlyph, play, 'lecture');
}
return renderIconButton(pauseGlyph, pause, 'pause');
};
​
const renderMuteButton = () => {
const { mute, unmute } = actions;
const { currentVolume } = core;
if (currentVolume === 0) {
return renderIconButton(muteGlyph, unmute);
}
return renderIconButton(volumeGlyph, mute);
};
​
const renderFullscreenButton = () => {
const { isFullscreen } = core;
if (!isFullscreen) {
return renderIconButton(fullscreenGlyph, () => { onFullscreenScreen(true); });
}
return renderIconButton(fullscreenExitGlyph, () => { onFullscreenScreen(false); });
};
​
const { setCurrentVolume } = actions;
const { currentVolume } = core;
return (
<div className={ style.ControlBar }>
<div className={ style.audio }>
{ renderMuteButton() }
<SoundBar setCurrentVolume={ setCurrentVolume } volume={ currentVolume } />
</div>
<div className={ style.controls }>
{ renderPlayButton() }
</div>
<div className={ style.settings }>
{ renderFullscreenButton() }
</div>
</div>
);
};
​
ControlBar.propTypes = {
actions: PropTypes.object.isRequired,
core: PropTypes.object.isRequired,
root: PropTypes.object.isRequired,
};
​
export default ControlBar;
versus :
export default class ControlBar extends Component {
static propTypes = {
actions: PropTypes.object.isRequired,
core: PropTypes.object.isRequired,
root: PropTypes.object.isRequired,
};
onFullscreenScreen(isFullscreen) {
const playerRoot = this.props.root;
if (isFullscreen && !screenfull.isFullscreen) {
screenfull.request(playerRoot);
} else {
screenfull.exit();
}
}
renderIconButton(glyph, action, label = false) {
let content = (
<Button modifier="icon" clickCallback={ action }>
<Icon glyph={ glyph } />
</Button>
);
if (label) {
content = <TooltipOrigin content={label}>{content}</TooltipOrigin>;
}
return content;
}
renderPlayButton() {
const { play, pause } = this.props.actions;
const { playerState } = this.props.core;
if (playerState === CoreStates.PAUSED) {
return this.renderIconButton(playGlyph, play, 'lecture');
}
return this.renderIconButton(pauseGlyph, pause, 'pause');
}
renderMuteButton() {
const { mute, unmute } = this.props.actions;
const { currentVolume } = this.props.core;
if (currentVolume === 0) {
return this.renderIconButton(muteGlyph, unmute);
}
return this.renderIconButton(volumeGlyph, mute);
}
renderFullscreenButton() {
const { isFullscreen } = this.props.core;
if (!isFullscreen) {
return this.renderIconButton(fullscreenGlyph, () => { this.onFullscreenScreen(true); });
}
return this.renderIconButton(fullscreenExitGlyph, () => { this.onFullscreenScreen(false); });
}
render() {
const { setCurrentVolume } = this.props.actions;
const { currentVolume } = this.props.core;
return (
<div className={ style.ControlBar }>
<div className={ style.audio }>
{ this.renderMuteButton() }
<SoundBar setCurrentVolume={ setCurrentVolume } volume={ currentVolume } />
</div>
<div className={ style.controls }>
{ this.renderPlayButton() }
</div>
<div className={ style.settings }>
{ this.renderFullscreenButton() }
</div>
</div>
);
}
}
We like the structure of a React Component. The PropTypes and default props can be inside the class thanks to ES7, it's not possible with pure functions. And, in this example particularly, we have many function to render sub-components.
We could simply disable this rule if we don't like, but we really want to understand that and we care about performance and React good practices. So, maybe you can help us.
How can you help me ? I would get other opinions about this interesting problematic. What are the arguments in favor of pure functions ?
Maybe the solution is not to change ControlBar Component in pure function but just to improve it. In this case, what would be your advices to do that ?
Thanks a lot for your help !

Categories

Resources