React Router Link can't clone function - reactjs

I'm practicing my React and stumbled upon something I couldn't find the answer on the internet.
The context is: I'm making a page using the Random User API. On the main page the user navigating can browse through random people and add them to a list. Additionally, the user can click on a person's name and go to a page with more details about that person. I configured the route using React Router Dom and a Link attached to the thumbnail. So far so good.
The problem is that when I decided to test some kind of constructors on this project, so when I fetch the person's info object from the API, I use a mini-constructor to add a few methods to it. Something like that:
function Person(person) {
return {
...person,
getFullName: () => `${person.name.first} ${person.name.last}`
}
}
const data = await fetchPerson();
addPerson(Person(data));
So, on the main page there's no problem. I call getFullName() inside a <p> tag and it displays the name perfectly. But when I try to send this constructed Person object containing both the information and the method via <Link>'s state, I get an error saying that the function can't be cloned. Note: I tried using a class constructor as well. The error is the following:
DataCloneError: Failed to execute 'pushState' on 'History': person => `${this.person.name.title} ${this.person.name.first} ${this.person.name.last}` could not be cloned.
I've tried declaring the function inside the Person functions and then referencing it inside the return and many other things but nothing seems to work.
If I could have a light on the question, it'd be nice!
Thanks! :))
EDIT:
I tried declaring the function outside the "constructor" and it worked. But it isn't very clean. If any other suggestions/explanations surface I'd really like to now!
const getFullName = (person) => `${person.name.title} ${person.name.first} ${person.name.last}`;
export class Person {
constructor(person) {
this.person = person;
this.getFullName = getFullName(person);
this.test = 'IOEJASIEPASKEKASÇLEKÇASKEA'
}
}
EDIT2: Tried with a function aswell, like the following, and worked. But I just can't understand the "cannot clone" error.
export function Person(person) {
const getFullName = (person) => `${person.name.title} ${person.name.first} ${person.name.last}`;
return {
...person,
fullName: getFullName(person) ,
}
}

Related

Not able to trigger function from event

I am not a skilled react programmer but still hope someone would care to explain what I am missing:
What I want
I would like to change accounts in Metamask, detect the "accountsChanged" event, and trigger the testFunction.
What works
I am able to trigger the testFunction by clicking the test function button.
I can detect account change (for some reason it is detected around 5 times every time I change).
What does not work
I am not able to trigger the testFunction upon account change and get the message TypeError: this.testFunction is not a function
Suspect there is something fundamental about react I am missing here...Thanks for all replies!
class App extends Component {
...
componentDidMount = async () => {
...
};
testFunction = async =>{
console.log("triggered the test function");
};
render() {
window.ethereum.on('accountsChanged', function (accounts) {
console.log("account change detected");
this.testFunction(); --> this is not working
});
return (
<div className="App">
<button type="button" onClick={this.testFunction}>test function</button>
</div>
);
}
}
You need to convert your normal function to arrow function. Because normal function derives this from the object which is calling it, but arrow function derives it's this from surrounding scope, hence in arrow function this will point to your class and will have access to the methods.
window.ethereum.on('accountsChanged', accounts => {
Also, you can continue using normal function, but in that case you can store the this in some other variable like that' or 'self and use it inside the normal function to call the methods of the class.
let that = this;
window.ethereum.on('accountsChanged', function(accounts){
that.testFunction() //this will work
I struggled to update the component of my app when an account was changed using MetaMask. What I did was what Vivek suggested: create a reference of this and then handle the callback. At the end my function using etherjs and the same event of metamask (ethereun.on('accountsChanged'..was this
const here = this
provider.provider.on('accountsChanged', function (accounts) {
console.log('Account changed!!')
here.currentAccount = accounts[0]
})
This code also work with Vue

Changing an input parameter within React component

I am going through the code of a project and I see the following code:
export const FileLink = React.memo(({ url, data, ext, linkContent }) => {
...
...
if (!url.includes('?')) {
url += '?'
}
if (!url.endsWith('?')) {
url += '&'
}
return <a href={`${url}file_format=${ext}`}>{linkContent}</a>
})
It is working fine and no bugs appear in app behavior. But url is a passed parameter and it is changed within the FileLink: from what I read React components should be pure functions. So, I wonder whether its ok to do that, under which circumstances, and if not - why? What can go wrong? Any examples of how it could mess up the app?
(If interested to see the full code: https://github.com/broadinstitute/seqr/blob/8b4419285dfac9365c5c500bbb87b89808c0cedd/ui/shared/components/buttons/ExportTableButton.jsx#L37)
url is a local variable. Reassigning that variable, which is all this code is doing to it, has no possibility of affecting code outside of this function call. It doesn't make the function impure.
Now, if you were passed in an object, and you started mutating that object, then that would break purity. Because if the component that passed you this object is still using it, then it can "see" that change. For example:
const Example = ({ someObjectProp }) => {
someObjectProp.name = 'bob';
}

React Navigation 4.x to React Navigation 5.x

The current problem I have is that i want to use navigation parameters to update the state
The tutorial in the link above uses React Navigation 4.x while I use React Navigation 5.x
Tutorial:
function onSaveNote() {
navigation.state.params.addNote({ noteTitle, noteValue })
navigation.goBack()
}
MyProject:
function onSaveAuction() {
navigation.navigate('Home', { auctionTitle, auctionValue }
}
This is the warning I would get whenever I used used the code for 4.x
I have tried using the second bullet point which is to use navigate instead but it still does not seem to work.
Any help would be appreciated.
There is nothing wrong with the syntax(except for the bracket you forgot to close). Your problem is with the data you are trying to pass. The warning tells you that you are trying to pass non-serializable values such as class instances, functions etc. So check again what are the values of auctionTitle and auctionValue.
We don't know your data, however you shouldn't pass functions or class in nav params.
To make sure that your data doesn't have non-serializable data, as mentioned above, you can try do a JSON.stringify(), then JSON.parse in next screen to see if this warning disappears.
The best solution is to check your data, but if you need to pass non-serializable data, feel free to use JSON.
I made a example to you:
Passing params:
function onSaveAuction() {
/* It will remove any functions, class or other non-serializable from params. */
const data = JSON.stringify({ auctionTitle, auctionValue });
navigation.navigate('Home', { data });
}
Home.js
function Home({ route, navigation }) {
/* Get the param, then parse to object */
const data = JSON.parse(route.params.data);
}

What is Reacts function for checking if a property applies?

Based off this Q&A:
React wrapper: React does not recognize the `staticContext` prop on a DOM element
The answer is not great for my scenario, I have a lot of props and really dislike copy-pasting with hopes whoever touches the code next updates both.
So, what I think might work is just re-purposing whatever function it is that React uses to check if a property fits to conditionally remove properties before submitting.
Something like this:
import { imaginaryIsDomAttributeFn } from "react"
...
render() {
const tooManyProps = this.props;
const justTheRightProps = {} as any;
Object.keys(tooManyProps).forEach((key) => {
if (imaginaryIsDomAttributeFn(key) === false) { return; }
justTheRightProps[key] = tooManyProps[key];
});
return <div {...justTheRightProps} />
}
I have found the DOMAttributes and HTMLAttributes in Reacts index.t.ts, and could potentially turn them into a massive array of strings to check the keys against, but... I'd rather have that as a last resort.
So, How does React do the check? And can I reuse their code for it?
The following isn't meant to be a complete answer, but something helpful for you in case I forget to come back to this post. The following code is working so far.
// reacts special properties
const SPECIAL_PROPS = [
"key",
"children",
"dangerouslySetInnerHTML",
];
// test if the property exists on a div in either given case, or lower case
// eg (onClick vs onclick)
const testDiv = document.createElement("div");
function isDomElementProp(propName: string) {
return (propName in testDiv) || (propName.toLowerCase() in testDiv) || SPECIAL_PROPS.includes(propName);
}
The React internal function to validate property names is located here: https://github.com/facebook/react/blob/master/packages/react-dom/src/shared/ReactDOMUnknownPropertyHook.js
The main thing it checks the properties against is a "possibleStandardNames" property-list here: https://github.com/facebook/react/blob/master/packages/react-dom/src/shared/possibleStandardNames.js
So to reuse their code, you can copy the property-list in possibleStandardNames.js into your project, then use it to filter out properties that aren't listed there.

Redux React - How to update store without and user event?

I am practising with React-Redux. I know this question sounds quite simple... But I can't figure out how to solve it. I have a function that will generate an object. I want to pass it to the store, but I don't know how to trigger the action without a user action (click button, for example).
const MyComponentA = () => (
<BuiltInCompoment propA={data} propB={ FunctionA } />
)
function FunctionA(object) {
...
FunctionB(object.property)
}
The function FunctionB will return an object (or maybe JSON file). So how do I pass that object (or json) to the store -so as to use it in another component?
Below I show my last attempt. I know it's wrong and has not too much sense. But maybe it clarifies a little bit more what I want (passing data to store so as to render it in a different component which is not a child of the container). The "BiultInComponent" can't be modified to include the prop "resultingData". I am a newbie so it's quite probable I am wrong about some points/assumptions.
class MyContainerA extends Component {
render()
return (
<BuiltInCompoment propA={data} propB={ FunctionA } />
)
}
function matchDispatchToProps(dispatch){
return bindActionCreators({resultingData: resultingData}, dispatch);
}
function FunctionA(object) {
...
var dataToPassToStore = FunctionB(object.property);
return this.props.resultingData(dataToPassToStore)
}
exports default connect(matchDispatchToProps)(MyContainerA)
Any suggestion will be welcome. Thanks.
Look into investigating dispatching actions and reducers. I believe both of these, which are core to redux, will help you achieve what you're trying to do.

Resources