How to check value of useRef.current.selectionStart in jest test - javascript

I need to test the values of useRef.current.selectionStart and useRef.current.selectionEnd once they have been changed on a onKeyDown interaction with an input.
index.tsx
import React, { useRef, useEffect } from 'react'
type Props {
value: string
...aBunchOfProps
}
const SomeComponent: FC<Props> = ({ value, ...aBunchOfProps }) => {
const inputRef = useRef(null)
const [caretPosition, setCaretPosition] = useState<CaretPosition | undefined>()
useEffect(() => {
if (!!caretPosition && !!value) {
let newCaretPosition = caretPosition.currentPosition
const shouldMoveCaretForward =
caretPosition.direction === 1 && value.charAt(caretPosition.currentPosition) === '/'
if (shouldMoveCaretForward) {
newCaretPosition++
}
inputRef.current.selectionStart = newCaretPosition <=== this is the line I want to test
inputRef.current.selectionEnd = newCaretPosition <=== this is the line I want to test
}
}, [caretPosition])
const someFunction = () => {
// calls setCaretPosition with new details
}
return (
...someAdditionalCode
<input
...someAdditionalProps
ref={inputRef}
value={value}
data-testid="input-field"
onKeyDown={() => someFuction()}
/>
...evenMoreCode
)
}
export default SomeComponent
index.test.tsx
describe('SomeComponent tests', () => {
it('should move cursor correctly', () => {
const { getByTestId } = render(<SomeComonent value="12/3" />)
const input = getByTestId('input-field')
fireEvent.keyDown(input, { key: '4' })
// expect(useRef.current.selectionStart).toEqual(5) <==== I want something like this
// expect(useRef.current.selectionEnd).toEqual(5) <==== I want something like this
})
})
Any suggestions would be helpful.

I had checked your code. It May be not possible to check useRef in testcase file. please check shown below image [![enter image description here and also share that document link, so it can be helpful to you.
document link: https://testing-library.com/docs/guiding-principles/

Related

Using state hooks to conditionally update an array

I want to add items to an array with the useState hook instead of doing array.push. This is the original code:
let tags = []
data.blog.posts.map(post => {
post.frontmatter.tags.forEach(tag => {
if (!tags.includes(tag)){
tags.push(tag)
}
})
})
This is one of several things I've tried with React:
const [tags, setTags] = useState([])
data.blog.posts.map(post => {
post.frontmatter.tags.map(tag => {
if (!tags.includes(tag)){
setTags(tags => [...tags, tag])
}
})
})
The "tags" state variable does not receive anything in the above example.
I have looked at a variety of similar threads but the problems and solutions there are difficult to translate to this situation.
You can try setting the tags state in initial render or on any event as per your requirement .
const [tags, setTags] = useState([]);
useEffect(()=>{
const arr=[];
data.blog.posts.map(post => {
post.frontmatter.tags.map(tag => {
if (!arr.includes(tag)){
arr.push(tag)
}
})
});
setTags([...arr]);
},[]);
Ok, I did understand what you wanted to do.
Here is the code and I did add some commest and there is also a working code sandbox
so it will show the "tags" you have on your state and when you click on the button it will filter and add those tags that are missing
import React, { useState } from "react";
//mock data.blog.posts
const data = {
blog: {
posts: [
{
frontmatter: {
tags: ["tag1", "tag2", "tag3"]
}
}
]
}
};
const App = () => {
const [tags, setTags] = useState(["tag1"]);
const filterTags = () => {
const myTags = ["tag1"];
let result;
data.blog.posts.map((post) => {
// check what tags are not included in tag stateon line 18
result = post.frontmatter.tags.filter((item) => !tags.includes(item));
});
// here it will show that 'tag2' and 'tag3' does not exist
console.log("result", result);
// here we are setting the state
setTags((oldState) => [...oldState, ...result]);
};
return (
<div className="App">
<h1>My tags</h1>
{tags.map((tag) => (
<h4>{tag}</h4>
))}
<button onClick={() => filterTags()}>add tags</button>
<hr />
<h1>My tags from posts</h1>
{data.blog.posts.map((posts) => {
return posts.frontmatter.tags.map((tag) => <div>{tag}</div>);
})}
</div>
);
};
export default App;
and here is the codeSandBox

How to pass a function to a component using arrow function?

FYI: I am using ES6 on ReactJS
I have a switcher. I need it to switch to the other side when clicked.
If click on the side that is currently active does nothing.
Here is my sample code
import { useState } from 'react'
const {isLeftButton, setIsLeftButton} = useState(true);
const toggleSwitcher = () => {
setIsLeftButton(!isLeftButton)
}
const home = () => {
...
return (
<CustomSwitcher isLeftButton={isLeftButton} toggleSwitcher={toggleSwitcher} />
)
...
}
export default Home
Here is the code inside the CustomSwitcher
const CustomSwitcher = (isLeftButton, toggleSwitcher) => {
const leftButton = () => {
if (isLeftButton !== true) {
toggleSwitcher()
}
}
const rightButton = (isLeftButton, toggleSwitcher) => {
if (isRightButton === true) {
toggleSwitcher()
}
}
return (
<div>
<CustomButton onClick={LeftButton}>Left Button</CustomButton>
<CustomButton onClick={rightButton }>Right Button</CustomButton>
</div>
)
}
export default CustomSwitcher
However I got this error
TypeError: toggleSwitcheris not a function
12 | const CustomSwitcher = () => {
13 |
14 | if (leftButton !== true) {
> 15 | toggleSwitcher()
| ^ 16 | }
17 | }
18 |
As I understand, when passing a function down to a component. The function is no longer a function.
And I don't think my code is good. If you can think of a better way to do so. Please contribute.
You are not using the correct way to access the props.
Try to replace
const CustomSwitcher = (isLeftButton, toggleSwitcher) => {
with
const CustomSwitcher = ({isLeftButton, toggleSwitcher}) => {
const CustomSwitcher = (isLeftButton, toggleSwitcher) => { ... }
is not the correct way to build a component.
Either use the props object
const CustomSwitcher = (props) => {
// props.toggleSwitcher
}
Or destructure props
cost CustomSwitcher = ({isLeftButton, toggleSwitcher}) => { ... }
You need to use useState inside a functional component. In your case, inside home. Hooks cannot be used at the global level.
Consequently, you need to define toggleSwitcher inside home also.

How can I call a function from another component react

I am trying to call a function from a different component but when I console.log('hi') it appear but it didn't call the messageContext.
Here is my follwing code from Invitees.js:
const [showPreview, setShowPreview] = useState(false);
const toggleUserPreview = () => {
setShowPreview(!showPreview);
};
{showPreview && (
<ResultsWrappers togglePreview={toggleUserPreview}>
<UserPreview
userInfo={applicant}
skillStr={applicant.Skills}
togglePreview={toggleUserPreview}
/>
</ResultsWrappers>
)}
Here is the component have the function I want to call UserPreview.js:
import { useMessageContextProvider } from "../context/MessageContext";
const UserPreview = ({ userInfo, skillStr, togglePreview }) => {
const messageContextProvider = useMessageContextProvider();
const messageUser = () => {
togglePreview();
messageContextProvider.updateActiveUserToMessage(userInfo);
console.log('hi');
};
...
};
Here is my messageContext:
import { createContext, useContext, useState } from "react";
const messageContext = createContext();
export const MessageContextProvider = ({ children }) => {
const [activeUserToMessage, setActiveUserToMessage] = useState({});
const [isOpenMobileChat, toggleMobileChat] = useState(false);
const updateActiveUserToMessage = (user) => {
setActiveUserToMessage(user);
};
return (
<messageContext.Provider
value={{
updateActiveUserToMessage,
activeUserToMessage,
isOpenMobileChat,
toggleMobileChat,
}}
>
{children}
</messageContext.Provider>
);
};
export const useMessageContextProvider = () => {
return useContext(messageContext);
};
When the messageContext called it should open the chatbox like this:
The code you showing is not enough to say it for 100%, but it seems like toggleUserPreview - function called twice, so it reverted to original boolean value.
One time as <ResultsWrappers togglePreview={toggleUserPreview}/>
and second time as <UserPreview togglePreview={toggleUserPreview}/>.

(React-Select hooks) How can I update state using Select's onChange render prop?

I've tried quite a few methods but I have not been able to get onChange to work. I'm working on a search-bar component that makes a fetch call after the user has not changed the search bar input for 3 seconds, but I am having issues changing the userSearchInput state hook which fires the api call in useEffect. Here is a minimized version of the code:
import React, { useState, useEffect } from "react";
import Select from "react-select";
export default function SearchBar() {
const [userSearchInput, setUserSearchInput] = useState("");
const [searchSuggestions, setSearchSuggestions] = useState([]);
useEffect(() => {
const searchSuggestions = async (searchInput) => {
console.log("api called");
const searchSuggestions = await fetch(
'API STUFF'
)
.then((response) => response.json())
.then((data) => {
setSearchSuggestions(data.quotes);
});
};
const timer = setTimeout(() => {
if (userSearchInput !== "") {
searchSuggestions("test");
}
}, 3000);
return () => clearTimeout(timer);
}, [userSearchInput]);
const handleSearchInputChange = (event) => {
setUserSearchInput(event.target.value);
console.log("input changed");
};
return (
<Select
options={searchSuggestions}
value={userSearchInput}
placeholder="Search a ticker"
onChange={handleSearchInputChange}
/>
);
}
Any ideas on where I'm going wrong?
select has value of object containing "label" and "value"
also, react select already returning value in an argument of function so all you have to do is to use it
const handleSearchInputChange = (selectedOptionObj) => {
setUserSearchInput(selectedOptionObj);
console.log("input changed");
};

React Context - State value is not up-to-date inside a function

I have the following context:
import React, { createContext, useState } from "react";
const OtherUsersContext = createContext(null);
export default OtherUsersContext;
export function OtherUsersProvider({ children }) {
const [otherUsers, setOtherUsers] = useState(new Map([]));
const addUser = (userId, userData) => {
setOtherUsers(
(prevOtherUsers) => new Map([...prevOtherUsers, [userId, userData]])
);
};
const updateUser = (userId, userData, merge = true) => {
...
};
const getUser = (userId) => otherUsers.get(userId);
const resetUsers = () => {
setOtherUsers(new Map([]));
};
return (
<OtherUsersContext.Provider
value={{
addUser,
updateUser,
getUser,
resetUsers,
}}
>
{children}
</OtherUsersContext.Provider>
);
}
In my app, when a user signs out, I need to reset this context's map, using the function "resetUsers".
Currently this is working good, but there has no sense to reset the map if it has no values, so I have changed the "resetUsers" function to:
const resetUsers = () => {
if(otherUsers.size) {
setOtherUsers(new Map([]));
}
}
And, this is not working good, because inside resetUsers, otherUsers.size is always 0. Something which disturbs me because outside the function, the value is the correct one...
...
const resetUsers = () => {
console.log(otherUsers.size); // 0
setOtherUsers(new Map([]));
};
console.log(otherUsers.size); // 5
return ( ...
Any ideas?
The functional updates part of the hooks docs. says:
If the new state is computed using the previous state, you can pass a function to setState.
So instead of just passing the new value to your setter, you can pass a function that depends on the previous state.
This means that you can do:
const resetUsers = () => {
setOtherUsers(prevOtherUsers => prevOtherUsers.size ? new Map([]): prevOtherUsers);
}
One tip, if you are not getting the most updated state value inside a function, then wrap it inside an useCallback.
Try this:
const resetUsers = useCallback(() => {
if (otherUsers.size > 0) {
console.log(otherUsers.size); // 5
setOtherUsers(new Map([]));
}
}, [otherUsers]);

Categories