How to convert class component to function component with hooks - reactjs

my code class component
raf = (callback) => {
var self = this;
if (this.requestFrame) raf.cancel(this.requestFrame);
this.requestFrame = raf(function () {
self.requestFrame = undefined;
callback();
});
}
update = (callback) => {
this.raf(function () { this._update(callback) });
}
my code function component
const raf = (callback) => {
if (Scrollbars.requestFrame) raf.cancel(Scrollbars.requestFrame);
Scrollbars.requestFrame = raf(() => {
Scrollbars.requestFrame = undefined;
callback();
});
};
const update = (callback) => {
raf(() => _update(callback));
};
I am switching from class component to function component but when i switch I get error "Maximum call stack size exceeded" in function raf.
Someone please help me.

In your function component add below code and try:
const update = (callback) => {
useEffect(() => {
raf(() => _update(callback));
}, []);
};

Related

Cleanup after a async function in useEffect

I'm migrating a React project to TypeScript but I'm struggling to type out this function and, subsequently the useEffect. I know that registerListener has to return a Promise but how do I access the value of that promise in useEffect?
const registerListener = async () => {
const listener = await someAsyncAction();
return { listener };
};
React.useEffect(() => {
const { listener } = registerListener();
return () => {
removeListener(listener);
};
}, []);
Have you tried to declare a return type on registerListener?
const registerListener = async (): Promise<{ listener: someAsyncActionReturnType }> => {
const listener = await someAsyncAction();
return { listener };
};

Test custom hook in ReactJS

I have this hook that should trigger beforeunload event when the compoenent is mounted and unmounted.
const UseHook = (fns: (e) => void) => {
const cb = useRef(fns);
useEffect(() => {
cb.current = fn;
}, [fn]);
useEffect(() => {
const onUnloadFN = (args: BeforeUnloadEvent) => cb.current?.(args);
window.addEventListener('beforeunload', onUnloadFN);
return () => {
window.removeEventListener('beforeunload', onUnloadFN);
};
}, []);
};
Now I want to test the hook using jest and enzyme:
import { mount } from 'enzyme';
import React from 'react';
const HookWrapper = () => {
useHook((e) => {
e.preventDefault();
e.returnValue = '';
});
return <div>component</div>;
};
describe('useHook', () => {
const location: Location = window.location;
delete window.location;
const mockPageReloading = jest.fn();
window.location = {
...location,
reload: mockPageReloading,
};
it('should mount', () => {
const mockedOnload = jest.fn();
window.addEventListener = jest.fn((event) => {
if (event === 'beforeunload') {
mockedOnload();
}
});
const wrapper = mount(<HookWrapper />);
expect(mockedOnload).toHaveBeenCalledTimes(1);
jest.restoreAllMocks();
console.log(wrapper.debug());
});
it('should unmount', () => {
const mockedOnload = jest.fn();
window.removeEventListener = jest.fn((event) => {
if (event === 'beforeunload') {
mockedOnload();
}
});
const wrapper = mount(<HookWrapper />);
wrapper.unmount();
expect(mockedOnload).toHaveBeenCalledTimes(1);
});
});
The first test pass, but the second retrieve that the event listener wasn't call on unmount (it was called 0 times).
Who can help with this?
Basically I want to test if the event was triggered on mount and also on unmount.
PS: this hook is also used to detect when user reload the page. If somebody has other idea how to test this hook, please let me know.

How to call a Firestore unsubscribe function from another function in a functional component?

I have the following function that runs upon a click. It basically starts a Firestore listener to grab messages. It also has an unsubscribe function declared which I am trying to call from another function:
const getMessages = (uid) => {
const ref = firebase.firestore().collection('Chats').doc(uid).collection('Messages');
const query = ref.where("uid", "==", uid).orderBy('timestamp', 'desc').limit(25);
const unsubFromMessages = query.onSnapshot((snapshot) => {
if (snapshot.empty) {
console.log('No matching documents.');
}
snapshot.docChanges().forEach((change) => {
if (change.type === 'removed') {
console.log(change.doc.data().content)
} else if (change.type === 'added') {
setMessages(prevFiles => ([...prevFiles, {
id: change.doc.id, body: change.doc.data()
}]))
// setTimeout( this.scrollToBottom(), 2000)
}
});
}, (error) => {console.log(error)});
}
As you can see inside of it, I declare a function to unsubscribe from the Firestore listener (const unsubFromMessages = query.onSnapshot). I want to be able to call this "unsubFromMessages" function upon another button click from another function which basically closes a chat.
Here's that closeChat function:
const closeChat = () => {
setMessages([]);
unsubFromMessages();
}
Unfortunately, the closeChat function can not access the unsubFromMessages function to unsubscribe from the Firestore listener. I get the following error:
Line 177:5: 'unsubFromMessages' is not defined no-undef
I know how to do it in a class component where I would simply declare the function as this.unsubFromMessages = ... and then call it from any other function but I can not figure out how to do it in a functional component. Please advise.
You could store the unsubFromMessages callback in a React ref and access it in the other click hander.
const unsubFromMessagesRef = React.useRef();
...
const getMessages = (uid) => {
...
const unsubFromMessages = query.onSnapshot((snapshot) => { ..... };
unsubFromMessagesRef.current = unsubFromMessages;
...
}
...
const closeChat = () => {
setMessages([]);
unsubFromMessagesRef.current && unsubFromMessagesRef.current();
}
Don't forget to unsubscribe when the component unmounts:
useEffect(() => {
return () => {
unsubFromMessagesRef.current && unsubFromMessagesRef.current()
};
}, []);

React Native: Cannot update a component from inside the function body of a different component

I have a very simple react native screen which looks as follows:
class BasicScreen extends React.Component {
state = {
data: [],
myItems: [],
};
componentDidMount() {
this.checkforItems();
}
checkforItems = async () => {
AsyncStorage.getItem('MyItems').then(item => {
if (item) {
this.setState({
myItems: JSON.parse(item),
});
} else {
console.log('No data.');
}
});
};
render() {
return (
<View>/* A detailed view */</View>
)
}
}
The problem is that it causes the following error:
Cannot update a component from inside the function body of a different component.
I'm not sure how to fix this. Please help.
This looks like it is just going to overwrite myItems with whatever the most recent mapped item is rather than appending each new item.
You might try mapping the items to an array in componentDidMount() and then set the state in a useEffect call.
const BasicScreen = () => {
const [myData, setData] = useState([]);
const [myItems, setItems] = useState([]);
const checkForItems = () => {
var storageItems = AsyncStorage.getItem("MyItems").then((item) => {
if (item) {
return JSON.parse(item);
}
});
setItems(storageItems);
};
useEffect(() => {
async function getItems() {
await checkForItems();
}
getItems();
}, []);
return (
<View>
<Text>{myItems[0]}</Text>
</View>
);
};
export default BasicScreen;

How use toHaveBeenCalled() inside FileReader.onload

I have component that load image and send image data up to parent component via props.handle function
How I can mock or call props.handle inside fileReader.onload
Maybe need use async, but I don't know how.
Was try repeat code from this question How do I test `image.onload` using jest in the context of redux actions (or other callbacks assigned in the action)
But it didn't help
ChildComponent.js:
class ChildComponent extends React.PureComponent {
constructor(props) {
super(props);
}
handleFileLoad = event => {
event.preventDefault();
const reader = new FileReader();
reader.onload = () => {
const data = reader.result;
this.props.parentHandleFunction(data)
}
reader.readAsDataURL(event.target.files[0]);
}
}
ChildComponent.propTypes = {
parentHandleFunction: PropTypes.func.isRequired,
};
ChildComponent.test.js:
describe('<ChildComponent />', () => {
let renderComponent;
let changeSpy;
beforeEach(() => {
changeSpy = jest.fn(value => value);
renderComponent = shallow(
<ChildComponent parentHandleFunction={changeSpy}/>,
);
});
it('should call handle file change', () => {
const childComponent = shallow(
renderComponent.find(ChildComponent).getElement(),
);
const file = new Blob(['image'], { type: 'image/jpeg' });
loadButton.find('input').simulate('change', {
preventDefault: () => {},
target: {
files: [file],
},
});
expect(changeSpy).toHaveBeenCalled();
});
})
Tests show errror:
'Expected mock function to have been called, but it was not called.'
update
I solve my problem by cuting onload logic in seprate function
## ChildComponent.js ##
class ChildComponent extends React.PureComponent {
constructor(props) {
super(props);
}
loadImage = data => {
const imageObject = {
url: data,
};
this.props.parentHandleFunction(
imageObject,
);
}
handleFileLoad = event => {
event.preventDefault();
const reader = new FileReader();
reader.onload = async () => this.loadImage(reader.result);
reader.readAsDataURL(event.target.files[0]);
}
}
ChildComponent.propTypes = {
parentHandleFunction: PropTypes.func.isRequired,
};
ChildComponent.test.js:
it('should call change spy function', () => {
renderComponent.instance().loadImage('mockImage');
renderComponent.update();
renderComponent.instance().forceUpdate();
expect(changeSpy).toHaveBeenCalled();
});

Resources