I am new to muaz-khan/RecordRTC, first time using it, copy/pasted the default implementation below and it already throwing error.
Why is this code snippet showing this error?
import { useState, useEffect } from "react";
import RecordRTC, { RecordRTCPromisesHandler } from "recordrtc";
export const useRecorderPermission = (
recordingType
) => {
const [recorder, setRecorder] = useState(null);
useEffect(() => {
getPermissionInitializeRecorder();
}, []);
const getPermissionInitializeRecorder = async () => {
let stream = await (navigator).mediaDevices.getUserMedia({
video: true,
audio: true,
});
let recorder = new RecordRTCPromisesHandler(stream, {
type: recordingType,
});
setRecorder(recorder);
};
return recorder;
};
https://prnt.sc/DpbBXr_9Fp0F
Related
I need to mock my custom hook when unit testing React component. I've read some stackoverflow answers but haven't succeeded in implementing it correctly.
I can't use useAuth without mocking it as it depends on server request and I'm only writing unit tests at the moment.
//useAuth.js - custom hook
const authContext = createContext();
function useProvideAuth() {
const [accessToken, setAccessToken] = useState('');
const [isAuthenticated, setAuthenticated] = useState(
accessToken ? true : false
);
useEffect(() => {
refreshToken();
}, []);
const login = async (loginCredentials) => {
const accessToken = await sendLoginRequest(loginCredentials);
if (accessToken) {
setAccessToken(accessToken);
setAuthenticated(true);
}
};
const logout = async () => {
setAccessToken(null);
setAuthenticated(false);
await sendLogoutRequest();
};
const refreshToken = async () => {
const accessToken = await sendRefreshRequest();
if (accessToken) {
setAccessToken(accessToken);
setAuthenticated(true);
} else setAuthenticated(false);
setTimeout(async () => {
refreshToken();
}, 15 * 60000 - 1000);
};
return {
isAuthenticated,
accessToken,
login,
logout
};
}
export function AuthProvider({ children }) {
const auth = useProvideAuth();
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
}
AuthProvider.propTypes = {
children: PropTypes.any
};
const useAuth = () => {
return useContext(authContext);
};
export default useAuth;
The test I've written
//mainPage.test.js
import React from 'react';
import { render, screen } from '#testing-library/react';
import Main from '../main/mainPage';
describe('Main when !isAuthenticated', () => {
beforeEach(() => {
jest.mock('../auth/useAuth', () => {
const originalModule = jest.requireActual('../auth/useAuth');
return {
__esModule: true,
...originalModule,
default: () => ({
isAuthenticated: false,
login: jest.fn,
logout: jest.fn
})
};
});
});
afterEach(() => {
jest.resetModules();
});
it('displays image and login form', () => {
render(<Main />);
const image = screen.getByRole('img');
const form = document.querySelector('[data-testid=loginForm]');
expect(image).toBeInTheDocument();
expect(form).toBeInTheDocument();
});
});
However, I get this error.
TypeError: Cannot read properties of undefined (reading 'isAuthenticated')
7 |
8 | function Main() {
> 9 | const isAuthenticated = useAuth().isAuthenticated;
| ^
10 | const location = useLocation();
11 |
12 | if (isAuthenticated)
at Main (src/main/mainPage.js:9:26)
at renderWithHooks (node_modules/react-dom/cjs/react-dom.development.js:14985:18)...
I've been also trying to use spyOn but nothing helped. What exactly do I need to change to make the mock work?
The mocking should happen before any describe block:
import React from 'react';
import { render, screen } from '#testing-library/react';
import Main from '../main/mainPage';
jest.mock('../auth/useAuth', () => {
const originalModule = jest.requireActual('../auth/useAuth');
return {
__esModule: true,
...originalModule,
default: () => ({
isAuthenticated: false,
login: jest.fn,
logout: jest.fn
})
};
});
describe('Main when !isAuthenticated', () => {
How do I use getStaticPaths when using Redux with Next.js?
I am using next-redux-wrapper to store my content and i am having trouble getting the data to display.
Please see my code example below
import { useSelector } from "react-redux";
import {getPageData} from '../redux/actions/pages'
import { useRouter } from "next/router";
import {wrapper} from '../redux'
import { getNavItems } from '../redux/actions/navItems';
import { getServiceData } from '../redux/actions/services';
import { getHomePage } from '../redux/actions/homePage';
export default function pageTemplate({page}) {
return(
<h1>{page.title}</h1>
)
}
export const getStaticPaths = async () => {
const pages = await getPageData()
const paths = Object.keys(pages).map((key) => {
const page = pages[key]
return{
params: {slug: page.slug.current}
}
})
return{
paths,
fallback: false
}
}
export const getStaticProps = wrapper.getStaticProps((store) => async (context) => {
await store.dispatch(getHomePage());
await store.dispatch(getServiceData());
await store.dispatch(getNavItems());
const slug = context.params.slug
console.log(slug)
const page = await store.dispatch(getPageData(slug))
return {
props: {page},
revalidate: 60
};
}
You can also see my redux action which is working fine as I have tested it in the sanity.io groq playground.
import * as actionTypes from '../actions/actionTypes';
import { groq } from 'next-sanity';
import { getClient } from '../../lib/sanity.server';
export const getPageData = (slug) => async (dispatch) => {
const query = groq`
*[_type == "page"]{
_id,
title,
slug
}
`;
const queryTwo = groq`
*[_type == "page" && slug.current != $slug]{
_id,
title,
slug
}
`;
if(slug) {
try {
// const client = ...
const pageData = await getClient().fetch(query);
dispatch({
type: actionTypes.GET_ALL_PAGES,
payload: pageData
});
} catch (err) {
console.log(err);
}
}
try {
// const client = ...
const pageData = await getClient().fetch(queryTwo);
dispatch({
type: actionTypes.GET_ALL_PAGES,
payload: pageData || pagesData
});
} catch (err) {
console.log(err);
}
};
I have faced the same issue before just releasing that you can use getStaticPaths without the need of the next-redux-wrapper
here is some example from a code that I've been working on lately
import { ReduxWrapper, store } from '#store/store'
export const getStaticPaths = async () => {
const postsSlugs = store
.getState()
.posts.posts.map((post) => ({ params: { slug: post.slug } }))
return {
paths: postsSlugs,
fallback: true,
}
}
export const getStaticProps = ReduxWrapper.getStaticProps(
(store) => async (context) => {
const slug = context.params?.slug
const post = store.getState().posts.post
return {
props: { post },
}
},
)
I hope that it may help you or anyone searching for the same issue
I am relatively new to react hooks and I am trying to create this custom hook to handle CRUD operations for my API.
This is the hook file:
import React, { useState, useEffect } from "react";
const useApi = (url, headers = { method: "GET" }, payload = null) => {
const [isLoading, setIsLoading] = useState(true);
const [apiData, setApiData] = useState(null);
const [serverError, setServerError] = useState(null);
const [api, setApi] = useState({});
const list = async () => {
try {
const resp = await fetch(url);
const data = await resp?.json();
setApiData(data);
setIsLoading(false);
} catch (error) {
setServerError(error);
} finally {
setIsLoading(false);
}
};
const create = async () => {
try {
const resp = await fetch(url, (headers = { method: "POST" }), payload);
const data = await resp?.json();
setApiData(data);
setIsLoading(false);
} catch (error) {
setServerError(error);
} finally {
setIsLoading(false);
}
};
setApi({
...api,
list: list,
create: create
});
return { isLoading, apiData, serverError, api };
};
export default useApi;
However, when I call api.list() in my main component inside a useEffect() hook, I get an infinite loop.
Sample component call:
import { useEffect } from "react";
import useApi from "./useApi";
export default function App() {
const {
isLoading: loading,
apiData: students,
serverError: error,
api
} = useApi("https://59f0f160ce72350012bec011.mockapi.io/students");
console.log(loading, students, error, api);
useEffect(() => {
api.list();
}, [api]);
return (
<div className="App">
<h1>list</h1>
{loading ? "loading" : students.map((x) => x.name)}
</div>
);
}
Here's the sandbox for it:
https://codesandbox.io/s/cocky-chebyshev-d9q89?file=/src/App.js:0-492
Can anyone help me understand the issue?
Thank you in advance!
This is what is causing the infinite loop:
setApi({
...api,
list: list,
create: create
});
You are not supposed to call setState() during a render.
In your case, you don't need to useState for the api object, you can just return it on every render:
return {
isLoading,
apiData,
serverError,
api: { list, create }
};
Here is a link to the fixed sandbox
Also, another warning: this code will repeatedly call api.list().
useEffect(() => {
api.list();
}, [api]);
Since api changes on every render, it will repeatedly call api.list().
This is the object that changes on every render:
return { isLoading, apiData, serverError, api };
You can ensure that you only call api.list() one time by using a ref.
import { useRef } from 'react'
// In the component
const gotRef = useRef(false)
useEffect(() => {
if (!gotRef.current) {
api.list();
gotRef.current = true
}
}, [api]);
I am a new React developer, implementing global state in my app. Im using useReducer with Context API to cache form search data, but I feel I'm using the reducer wrong, even if it works. I am preparing payload BEFORE calling dispatchSearchData, instead of doing it directly inside reducer:
import React, { createContext, useReducer, useMemo, useEffect } from "react";
const initialData = {
from: "",
to: "",
date_go: "",
date_back: "",
passengers: "",
};
const dataReducer = (searchData, newData) => {
if (newData === null) {
localStorage.removeItem("currentSearchData");
return initialData;
}
return { ...searchData, ...newData };
};
const localData = JSON.parse(localStorage.getItem("currentSearchData"));
export const SearchDataContext = createContext({});
export const SearchDataProvider = (props) => {
const [searchData, dispatchSearchData] = useReducer(dataReducer, localData || initialData);
const searchDataValue = useMemo(
() => ({
searchData,
setSearchData,
}),
[searchData, setSearchData],
);
useEffect(() => {
localStorage.setItem("currentSearchData", JSON.stringify(searchData));
}, [searchData]);
return <SearchDataContext.Provider value={searchDataValue}>{props.children}</SearchDataContext.Provider>;
};
An example of calling it:
let search = (e) => {
e.preventDefault();
dispatchSearchData(formData);
setServiceData(null);
}
I'm learning Hooks with React and I'm trying to do a simple fetch to an API then I'm trying to save that data inside a Hook but It has not been possible for me.
import React, { useState, useEffect } from "react";
function useDogs() {
const [dogs, setDogs] = useState({
data: {}
});
useEffect(() => {
const fectData = async () => {
const data = await fetch("https://dog.ceo/api/breeds/image/random");
setDogs({ data: data.url });
console.log(data.url);
};
fectData();
}, []);
}
function Dogs() {
const dogs = useDogs();
console.log("dogs", dogs);
return <ul>{dogs} dogy</ul>;
}
export default Dogs;
In component Dogs() I'm having dogs as undefined
You aren't returning dogs from your useDogs hook.
(Also, to get at the response data, you need to await on .json() from the fetch response.)
import React, { useState, useEffect } from "react";
function useDogs() {
const [dogs, setDogs] = useState({
data: {},
});
useEffect(() => {
const getData = async () => {
const resp = await fetch("https://dog.ceo/api/breeds/image/random");
const data = await resp.json(); // <-- this
setDogs({ data });
console.log(resp, data);
};
getData();
}, []);
return dogs; // <-- this
}
function Dogs() {
const dogs = useDogs();
console.log("dogs", dogs);
return <ul>{dogs} dogy</ul>;
}
export default Dogs;
Custom Hooks are just javascript functions if you want to assign them to a variable you need to return something in your custom hook,
In some situations you need to set Something into your custom hook which you can do that by returning both value and setValue functions, for example in your case like below:
import React, { useState, useEffect } from "react";
function useDogs() {
const [dogs, setDogs] = useState({
data: {},
});
useEffect(() => {
const getData = async () => {
const resp = await fetch("https://dog.ceo/api/breeds/image/random");
const data = await resp.json(); // <-- this
setDogs({ data });
console.log(resp, data);
};
getData();
}, []);
return [dogs, setDogs]; // <-- this
}
and when you want to use it you just destructure it like this:
function Dogs() {
const [dogs, setDogs] = useDogs();
console.log("dogs", dogs);
return <ul>{dogs} dogy</ul>;
}
export default Dogs;
now you can read the data and also in future cases if you'ld like you can set the data too,