Window is undefined in NextJS + Tauri - reactjs

What I'm trying to do:
I was using the appWindow from Tauri to access the appWindow.minimize(), appWindow.toggleMaximize(), and appWindow.close() to create a custom title bar.
What the code looks like:
import { appWindow } from "#tauri-apps/api/window";
const CustomTitleBar = () => {
const hasLoaded = hasLoadedCSR(); // custom hook for checking if component has mounted using useEffect
if (typeof window === "undefined") return <></>; // 1st attempt to disable SSR for this component
if (!hasLoaded) return <></>; // 2nd attempt to disable SSR for this component
return (
<>
<div data-tauri-drag-region className="titlebar">
<button
className="titlebar-button"
id="titlebar-minimize"
onClick={() => {
console.log("Clicked");
appWindow.minimize();
}}
>
<img
src="https://api.iconify.design/mdi:window-minimize.svg"
alt="minimize"
/>
</button>
<button
className="titlebar-button"
id="titlebar-maximize"
onClick={() => appWindow.toggleMaximize()}
>
<img
src="https://api.iconify.design/mdi:window-maximize.svg"
alt="maximize"
/>
</button>
<button className="titlebar-button" id="titlebar-close">
<img
src="https://api.iconify.design/mdi:close.svg"
alt="close"
onClick={() => appWindow.close()}
/>
</button>
</div>
</>
);
};
export default CustomTitleBar;
I basically did 2 attempts to solve the problem because I definitely think this is caused by SSR as mentioned by FabianLars in a similar question.

To fix the problem, I basically created another component using the dynamic function to force CSR for CustomTitleBar.
import dynamic from "next/dynamic";
const DynamicCustomTitleBar = dynamic(() => import("./CustomTitleBar"), {
ssr: false,
});
export default DynamicCustomTitleBar;

How I got around this issue is as follows.
If and when I use any of the #tauri-apps/api exports I wrap them in a component that I then import using the dynamic import flow without ssr. This is mainly to keep my project organized because I just create sort of a wrapper for every function that I might want to use from tauri-apps/api.
So I would make the following folder structure:
const TauriApiWrapComponent = dynamic(() => import("./TauriApiWrapComponent"), {
ssr: false,
});

Related

How to write test for a button inside a list tag? Unable to get the button element inside a ui tag?

checkResult is a helper function which is imported in my component.jsx
component.jsx
return(
<ul>
{options.map((option) => {
return (
<li key={option.value}>
<button
data-testid="unlock-btn"
onClick={() => {
checkResult()
? lunch(option.value)
: showError();
}}
>
{option.label}
</button>
</li>
);
})}
</ul>;
)
my test
import * as helper from "../helpers/checkResult";
it("fires action when lunch is clicked", async () => {
const spy = jest.spyOn(helper, 'checkResult');
let component;
await act(async()=>{
component = <component /> ;
})
await expect(screen.queryByTestId("unlock-btn"));
fireEvent.click(screen.queryByTestId("unlock-btn"));
expect(spy).toHaveBeenCalled();
});
this is the error i'm getting
Unable to fire a "click" event - please provide a DOM element.
i have also provided my getComponent Method above
You're not providing options to the component so it has nothing to render. You're also using a map to render a list of items all of which have the same id. You should do something like
map((option, index) => {
return (
<li key={option.value}>
<button
data-testid=`unlock-btn-${index}`
This way you can target each individual option by ID in your test.
Edit: Your fireEvent is not defined in your example either.
The right way would be using the aria-label and attributes to be able to select those buttons without the need of data-testid.
<button
onClick={() => { checkResult() ? lunch(option.value): showError();}}
name={option.label} // assuming the labels are unique
>
{option.label}
</button>
then:
import React from 'react';
import { render, screen, fireEvent } from '#testing-library/react';
it('Should do some test', ()=>{
render(<MyComponent/>)
const button = screen.getByRole('button', {name: "some-label"})
fireEvent.click(button)
expect(....).toBe(...)
}

Defining Components in React

I am creating a react website in which many pages have a specific button that should look the same for all. Should this button be its own component? If so, how would I specify the onClick events to be different for each button if it is a component?
Yes, it should be its own component.
Create it in a separate file so you can import them where you need to use.
The component should receive a onClick prop that you pass to the internal button tag.
See this example: https://reactjs.org/docs/components-and-props.html#composing-components
export const Button = ({ label, onClick, disabled }) => {
return (
<button
onClick={onClick}
disabled={disabled}
>
{label}
</button>
)
}
and then you can export this component inside any of the components you want, and pass in values like onClick and label to make it more dynamic
export const DemoFunction () => {
const onClickHandler = () => {}
return (
<Button label="Click" onClick={onClickHandler} />
)
}

How can i get multiple recoil atoms when using components multiple?

In some components i am using recoil atoms to manage my states. One example is my modal component. It look something like this:
export const modalState = atom({
key: "modalState",
default: false
})
export const useToggleModalState = () => {
const setModalState = useSetRecoilState(modalState)
return (state, callback) => {
setModalState(state)
if (callback) {
callback()
}
}
}
export const Modal = (props) => {
<Transition show={modalState}>
<Dialog>
<Dialog.Title>My Modal Headline</Dialog.title>
<Dialog.Description>My Modal Description</Dialog.Description>
</Dialog>
</Transition>
}
and i am using this modal like this:
const toggleModalState = useToggleModalState();
return (
<Modal />
<Button text="Close Modal" onClick={() => toggleModalState(false)} />
)
however, if I use the modal multiple times, the modal is automatically duplicated, but I still use only one state for all modals. Of course, I don't want that. If I use a component multiple times, I want the state to be created multiple times, so that I can change the state of each component individually.
I have read that there are also atomFamilys. Could I use these at this point? What should my code look like then? Can multiple atoms also be created automatically if I use a component multiple times?
Why do you want to use recoil for that? The state of the modal is tied to the modal itself, it doesn't need to access global state.
You can just use useState to determine if you want to show a modal within a component:
export const Modal = (props) => {
<Transition show={props.show}>
<Dialog>
<Dialog.Title>My Modal Headline</Dialog.title>
<Dialog.Description>My Modal Description</Dialog.Description>
</Dialog>
</Transition>
}
export const ComponentWithModal = () => {
const [showModal, setShowModal] = useState(false);
return (
<>
<Modal show={showModal}/>
<Button text="Open Modal" onClick={() => setShowModal(true)} />
<Button text="Close Modal" onClick={() => setShowModal(false)} />
</>
)
}

React Facebook Login popping up on page load

I have this code in React:
import React from 'react';
import { FACEBOOK_ID } from '../../../config/credentials';
import FacebookLogin from 'react-facebook-login/dist/facebook-login-render-props';
const responseFacebook = response => {
console.log(response);
};
const FacebookLoginButton = () => (
<div>
<FacebookLogin
appId={FACEBOOK_ID}
autoLoad
callback={responseFacebook}
render={renderProps => (
<button onClick={e => {
console.trace();
}
}>Login via FB</button>
)}
/>
</div>
);
export default FacebookLoginButton;
This is just a simple react facebook login, now my problem is when I reload my local development site, The facebook authorization always popup on load or when I am currently logged in, it automatically logins the user and returns to the console the information from Facebook. is that the normal behavior of this library? I am new to react so any comments would really be appreciated.
Edit:
I've researched a few videos like this one: https://www.youtube.com/watch?v=ea9KyE78qKI&t=10s
It seems like his facebook component popups too when he visit it on his local site at around 12:48 mark but got blocked because Chrome doesn't allow automatic popups.
Make autoLoad as false.
<FacebookLogin
appId={FACEBOOK_ID}
autoLoad={false}
callback={responseFacebook}
render={renderProps => (
<button onClick={e => {
console.trace();
}
}>Login via FB</button>
)}
/>
Just remove the autoLoad option will work fine.
By mentioning autoLoad will take autoLoad = true by default.
autoLoad = false or remove autoload will do the same thing.
Hope this will clear more.
const FacebookLoginButton = () => (
<div>
<FacebookLogin
appId={FACEBOOK_ID}
callback={responseFacebook}
render={renderProps => (
<button onClick={e => {
console.trace();
}
}>Login via FB</button>
)}
/>
</div>
);

Using Unstated with TypeScript and React, how to get a typed container instance inside <Subscribe> children?

In my React app, I'm using Unstated to manage shared state, but I'm running into a problem using this with TypeScript: the <Subscribe> component passes me an instance of my state that's typed as Container<any>. This means it will need to be cast to my own type, e.g. Container<MyState>, before I can safely use it.
If instead I wanted Unstated to pass me an already-typed instance of my container, how should I wrap and/or fork the Unstated source and/or its typings file so that when I get the Container instance, it's typed as Container<MyState>?
BTW, the particular reason why I want to get a passed-in typed container is so that I can use destructuring of complex state without having to switch to using the block form of fat-arrow functions which is much more verbose.
Here's a simplified example of the code that I'd like to be able to write:
function Counter() {
return (
<Subscribe to={[CounterContainer]}>
{({increment, decrement, state}) => (
<div>
<button onClick={() => decrement()}>-</button>
<span>{state.count}</span>
<button onClick={() => increment()}>+</button>
</div>
)}
</Subscribe>
);
}
And here's the current way I'm writing it, inside a simplified Unstated example using TypeScript:
import React from 'react';
import { render } from 'react-dom';
import { Provider, Subscribe, Container } from 'unstated';
interface CounterState {
count: number
};
class CounterContainer extends Container<CounterState> {
public state = {
count: 0
};
public increment() {
this.setState({ count: this.state.count + 1 });
}
public decrement() {
this.setState({ count: this.state.count - 1 });
}
}
function Counter() {
return (
<Subscribe to={[CounterContainer]}>
{(counter: CounterContainer) => {
const {increment, decrement, state} = counter;
return (
<div>
<button onClick={() => increment()}>-</button>
<span>{state.count}</span>
<button onClick={() => decrement()}>+</button>
</div>
)
}}
</Subscribe>
);
}
render(
<Provider>
<Counter />
</Provider>,
document.getElementById('root')
);
So looking at the PR that originally added the typescript definition, it is noted that they couldn't find a way to provide the typing automatically that you are looking for, since something like $TupleMap doesn't exist in TS like it does in Flow, and you instead have to manually provide the typing.
If your primary motivation is avoiding the extra {} with the arrow function, you can manually provide the typing and still do the same destructuring. So from your example:
function Counter() {
return (
<Subscribe to={[CounterContainer]}>
{({increment, decrement, state}: CounterContainer) => (
<div>
<button onClick={() => increment()}>-</button>
<span>{state.count}</span>
<button onClick={() => decrement()}>+</button>
</div>
)
}
</Subscribe>
);
}
works as well. Maybe there is a way to type this automatically in Typescript, but it's beyond me.

Resources