How to test drag and drop with React Dnd - reactjs

I created a drag and drop interaction in React using react-dnd library and I want to test that on drop, a certain function is called.
However, I'm blocked on how to setup the test in order to make it pass.
My code until now is:
it('should ', () => {
const dropTarget = mountWithTheme(
withRedux(store, <DnDCard fileId={folder._id} onClick={onClick} />),
)
const dragSource = mountWithTheme(
withRedux(store, <DnDCard fileId={img._id} onClick={onClick} />)
)
var backend = getBackendFromInstance(<DnDCard fileId={folder._id} onClick={onClick} /> )
var manager = backend.manager
simulateDragDropSequence(dropTarget.instance(), dragSource.instance(), backend)
})
And in that simulateDragDropSequence I get an error saying that:
Cannot read property 'getHandlerId' of null.
That's because the function tries to call getHandlerId on the first 2 parameters of it, but apparently the instance doesn't provide that.
That mountWithTheme is adding Redux with a Provider for our component so it works fine.
What would be the issue here?
LE: Read here that getHandlerId is an instance method available when setting up the component with Legacy Decorator API but I did it using Hooks ( Top-Level API ). I think this might be the reason this is not quite testable.

Related

Algolia: Export React Instant Search results

Is there any way to export Algolia's React Instant Search results to a CSV? I've tried using the react-csv package, but it doesn't work with Algolia's Hit Component. The package requires data as props, but the data is constantly changing since it's React Instant Search.
What I mean by constantly changing is that on page load, you're given the entire index of records found, then you can narrow down the results with the search bar or other filtering components.
I've gone down the Google rabbit hole looking for information about exporting Algolia's search results as a CSV, but I haven't found anything regarding React Instant Search—unless I completely missed it.
Has anyone tried this before? If so, could you point me in the right direction regarding documentation or examples?
Not sure if this solves your problem but one possible way is to use the StateResults widget. The StateResults widget provides a way to access the searchState and the searchResults of InstantSearch.
Here I will create a custom StateResults component in the form of a download button and then connect it using the connectStateResults connector.
I have attached a demo below as well.
For simplicity I didn't format the data to be fed into the CSV builder.
// 1. Create a React component
const StateResults = () => {
// return the DOM output
};
// 2. Connect the component using the connector
const CustomStateResults = connectStateResults(StateResults);
// 3. Use your connected widget
<CustomStateResults />
In your case something like
const StateResults = ({ searchResults }) => {
const hits = searchResults?.hits;
return (
<div>
<button>{hits && <CSVLink data={hits}>Download CSV</CSVLink>}</button>
</div>
);
};
const DownloadButton = connectStateResults(StateResults);
//in your JSX then <DownloadButton />

How to change z index of components in React?

I forked the Keeper app project from Angela Yu's course on Udemy and made some modifications. Here is the link: https://codesandbox.io/s/keeper-app-part-2-completed-forked-9zks1?file=/src/components/Note.js
I wanted to be able to move the notes around the canvas, and while I was able to do that, I'm having a problem with the stacking of the notes. I want the selected note to be on top of all the other notes. I've tried tinkering with the z-index value by creating a useRef called noteRef and typing:
noteRef.current.style.zIndex = 9999
inside the handleOnClick function, which is called during onMouseDown. However, it doesn't really do anything. I tried having that and then typing
noteRef.current.style.zIndex = 1
inside the handleOnUp function, and while I was able to have the selected note on top of the others while moving, obviously it just goes right back below the notes when I release the mouse.
I've also tried using useEffect but it also didn't change anything. I was wondering if there is a way to access functions from the App component (where the note components reside).
**EDITED
Here is my example code sandbox.
How about managing notes` z-index with state in parent component?
In below's my example, i used useState in App component and made stackNote function for handling child component's style.
function App () {
const [zIndex, setZindex] = useState(1);
const stackNote = (ref) => {
ref.current.style.zIndex = zIndex;
setZindex((zIndex) => zIndex + 1);
};
return (
<div>
<Header />
{notes.map((noteItem) => (
<Note
stackNote={stackNote}
key={noteItem.key}
title={noteItem.title}
content={noteItem.content}
/>
))}
<Footer />
</div>
);
}
Then, in the Note component, just call stackNote with noteRef in your handleOnClick.
function handleOnClick(e) {
e.preventDefault();
props.stackNote(noteRef);
offsetX = e.pageX - notePos.x;
offsetY = e.pageY - notePos.y;
console.log("Mouse is clicked!");
document.addEventListener("mousemove", handleOnMove);
document.addEventListener("mouseup", handleOnUp);
}
And don't forget should erase previous codes something like noteRef.current.style.zIndex = ...
Although ref existed in the original code, so i used as it is, but it doesn't seem necessary to use it.

React Context always returns EMPTY

I have a Search parent component and a SideBar child component, I am trying to get context in SideBar, but everytime it returns empty.
I followed the tutorial exactly like: https://itnext.io/manage-react-state-without-redux-a1d03403d360
but it never worked, anyone know what I did wrong?
Here is the codesandbox link to the project: https://codesandbox.io/s/vigilant-elion-3li7v
I wrote that article.
To solve your specific problem:
When using the HOC withStore you're injecting the prop store into the wrapped component: <WrappedComponent store={context}.
The value of the prop store is an object that contains 3 functions: get, set, and remove.
So, instead of printing it, you should use it. For example this.props.store.get("currentAlbums") or this.props.store.set("currentAlbums", [album1, album2]).
This example is forked by your code: https://codesandbox.io/s/nameless-wood-ycps6
However
Don't rewrite the article code, but use the library: https://www.npmjs.com/package/#spyna/react-store which is already packed, tested, and has more features.
An event better solution is to use this library: https://www.npmjs.com/package/react-context-hook. That is the new version of the one in that article.
This is an example of a sidebar that updates another component content: https://codesandbox.io/s/react-context-hook-sidebar-xxwkm
Be careful when using react context API
Using the React Context API to manage the global state of an application has some performance issues, because each time the context changes, every child component is updated.
So, I don't recommend using it for large projects.
The library https://www.npmjs.com/package/#spyna/react-store has this issue.
The library https://www.npmjs.com/package/react-context-hook does not.
You pass the store as a prop, so to access it, you need this.props.store in your SideBar.
Not this.state.store
Create a wrapping App component around Search and Sidebar:
const App = props => (
<div>
<Search />
<SideBar />
</div>
);
export default createStore(App);
Now you can manipulate state with set and get that you have available in child components Search and Sidebar.
In Search component you can have something like:
componentDidMount() {
this.props.store.set("showModal", this.state.showModal);
}
also wrapped with withStore(Search) ofc.
and in SideBar you can now call:
render() {
return (
<div>
{"Sidebar: this.state.store: ---> " +
JSON.stringify(this.props.store.get("showModal"))}
}
</div>
);
}
and you will get the output.

ReactTable and custom server side data update

Has anybody ever used this awesome react components processing server side data?
The solution given here is excellent if you don't need to manually update the data.
I would need to refresh the data not only when changing page/pageSize/sorting/filtering, but also after some intervalled time, to see if data got changed.
Also I have an extension of the table that allows the user to do a full text search on all columns so I would need to update the data when the user changes the content of the custom search box too.
I had the same problem as yours and I started looking for asynchronous tests with ReactJs and I found this article that was very useful. The key part was to use "update" method on the Enzyme component, like below:
const items = ['die Straße', 'die Adresse', 'die Nationalität'];
jest.useFakeTimers();
describe('<DelayedList />', () => {
test('it renders the items with delay', () => {
const component = shallow(
<DelayedList items={items} />
);
jest.runAllTimers();
component.update(); // <--- force re-render of the component
expect(component.state().currentIndex).toEqual(items.length);
expect(component).toMatchSnapshot();
});
For more information, see this medium article.

Will ReactDOM.hydrate() trigger lifecycle methods on the client?

From the React 16 docs about ReactDOM.hydrate(),
Same as render(), but is used to hydrate a container whose HTML contents were rendered by ReactDOMServer. React will attempt to attach event listeners to the existing markup.
Will ReactDOM.hydrate() also trigger lifecycle methods on the client such as componentWillMount(), componentDidMount() during initial render?
Will render() method be called on the client during hydration? I suppose not, because that's the difference between ReactDOM.render() and ReactDOM.hydrate()?
If render method won't be called on the client, we wouldn't expect componentDidMount() lifecycle method to be triggered.
If none of the lifecycle methods are called on the client, how would we know when has React finished rendering. I suppose the callback in the following syntax:
ReactDOM.hydrate(element, container[, callback])
I want to understand if there are lifecycle methods / hooks (which give more control over the application) available when React is "attempting to attach event listeners to existing markup".
Since ReactDOM.hydrate is (and should be) called on the client then YES it is supposed to run componentDidMount. componentWillMount is already called when rendered on the server.
componentDidMount does not run on the server, therefore when you call hydrate, the app runs the event.
Think about hydrate as a different render method. It does render but not in the same way. It looks for mismatches between your server rendered React and your client React. It does not render everything again.
React expects that the rendered content is identical between the server and the client. It can patch up differences in text content (such as timestamps), but you should treat mismatches as bugs and fix them
However you might want to do some crazy stuff like rendering something completely different on the client side (than what was rendered on the server). For this pay attention to this paragraph
If you intentionally need to render something different on the server and the client, you can do a two-pass rendering. Components that render something different on the client can read a state variable like this.state.isClient, which you can set to true in componentDidMount(). This way the initial render pass will render the same content as the server, avoiding mismatches, but an additional pass will happen synchronously right after hydration. Note that this approach will make your components slower because they have to render twice, so use it with caution.
So as you can see it does a render pass. If there are no mismatches React is optimized for that.
I hope it was clarifying. I speak from experience with React SSR and basic understanding of reading the docs.
The rendered elements probably aren't same between server and client, because initially the elements are rendered into texts at the server in memory, therefore they are not mounted. When the content is moved to client, it can be re-attached to react via hydrate which is fake "render" to wire with the rest of react functionalities, such as events.
In order to tell when it's hydated, here's a piece from internet which I found clearly stated the above rational. https://dev.to/merri/understanding-react-ssr-spa-hydration-1hcf?signin=true
const HydrateContext = createContext('hydrated')
export function useIsHydrated() {
return useContext(HydrateContext)
}
export function IsHydratedProvider({ children }) {
const [isHydrated, setIsHydrated] = useState(false)
useEffect(() => {
setIsHydrated(true)
}, [])
return (
<HydrateContext.Provider value={isHydrated}>
{children}
</HydrateContext.Provider>
)
}
To use it,
function MyComponent() {
const isHydrated = useIsHydrated()
return !isHydrated ? 'Initial render' : 'SPA mode'
}
function App() {
return (
<IsHydratedProvider>
<MyComponent />
</IsHydratedProvider>
)
}
It feels to me, any rendered component teleports from the server to the client.
p.s Here's another article which talks about the second render after the mount, https://medium.com/swlh/how-to-use-useeffect-on-server-side-654932c51b13
I read the type of ReactDOM.hydrate in TypeScript system:
(
element: SFCElement<any> | Array<SFCElement<any>>,
container: Container| null,
callback?: () => void
): void;
And example to the above declaration:
ReactDOM.hydrate(
<App />, // element
document.getElementById('root'), // container
() => { // callback
/* do what you want after hydration */
}
);

Resources