React (not sure where a variable comes from) - reactjs

I am new to React and am trying to understand React-Popper. This is some code from
https://www.npmjs.com/package/react-popper
Where are the values for 'ref', 'style', 'placement' and 'arrowProps' coming from and how would I edit them? I understand that you can use this.props and attributes to pass values to Components but i dont understand where the values to be inserted into the function is coming from.
import { Manager, Reference, Popper } from 'react-popper';
const Example = () => (
<Manager>
<Reference>
{({ ref }) => (
<button type="button" ref={ref}>
Reference element
</button>
)}
</Reference>
<Popper placement="right">
{({ ref, style, placement, arrowProps }) => (
<div ref={ref} style={style} data-placement={placement}>
Popper element
<div ref={arrowProps.ref} style={arrowProps.style} />
</div>
)}
</Popper>
</Manager>
);

What you're seeing here is an arrow function being combined with destructuring assignment and React Render Props. So it's a lot in one code example.
From your question, I think what's confusing you most is the destructuring assignment. So here is an example which I hope will help you:
var foo = ({a, b}) => a + b;
var x = {
a: 1,
b: 2
};
console.log(foo(x));
# Output is 3
This is because destructuring assignment assigns the values from the object into the variables a and b as if they were function parameters. The same thing is happening with the props object on the React components and that's why you don't see props.ref, etc.

They are render props for the Popper component. They are all parameters of the render prop function defined in the Popper file you can find on the GitHub for this package. I'm not familiar with this specific library, but basically they are being passed to that function, and they need to be there as it's defined or it will throw an error. You should be able to compute your own values for styles and whatnot but again I'm not familiar with this package.

Related

How to return multiple times same component but with independant state?

Hi guys in react i want to do something like render 2 times a component in same render function like this
const Main()=>{
const [names, setNames] = useState([]);
const [selected, setSelected] = useState(-1);
return(
<Component1 name={names[selected].name}
setName={setNames}/>
<Component1 name={names[selected].name}
setName={setNames}/>
)
}
This where i passed setNames from Main() to Component1 to handle state
and pass names from Main() then map it in select of Component1
<StyledSelect
ml="10px"
onChange={(e) => {
setName(e.target.value);
value={name}>
{names.map(({ name}) => {
return (
<option key={name} value={name}>
{name}
</option>
);
})}
my component has an html select that has names in it, when i choose one it trigger a it in database and retrieve name data (age, location), the problem is when i select name on my second Component1, it will also select the same name and retrieve same name data on my first Component1, and i want them to be independent. How ?
I tried to put key like that
return(
<Component1 key="compare" name={name}/>
<Component1 key="compared" name={name}/>
)
but nothing changes
Thanks
This question is worded incorrectly (this has nothing to do with state and everything to do with props and how React renders) and is missing information, but here are a few pieces of information that might help:
Keys don't change anything on the front-end; they are a tool React uses to improve performance on the back-end. Click here to read the docs for more information on keys and how React uses them.
React components are JavaScript functions and can therefore only return 1 thing. The code sample you shared would not work in React (or JavaScript). In order to return multiple items, it is possible to wrap them in an array or a React Fragment. Consider this example:
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
If you want to make the dropdowns different in appearance and/or functionality, the name prop will need to be different. Right now, both instances of Component1 are being given the same name property, which is why they probably look and behave identically.
(i would like can comment but i can't)
i am not sure (i must see the complete code) but i think that:
return(
<Component1 key="compare" name={name}/>
<Component1 key="compared" name={name}/>
)
must be
return(
<Component1 key="compare" name={name1}/>
<Component1 key="compared" name={name2}/>
)
this way they don't share the same variable
Where is it that you're making the call and setting the name? Since you're passing the same prop name to both of them they will be the same.
Either you set name inside Component1 itself or you make a different state for each Component1.

How does the 'key' prop work in a non-dynamic child component and why is it essential for rendering updates?

I understand that the special 'key' prop when used with child components that are created dynamically from arrays helps React to uniquely identify components and render updates efficiently. But I would like to know when and why would it be necessary to use the key prop for a 'non-dynamic' component.
My application uses a Reducer and useContext hook to manage state for a Functional Component A. The state object has a maximum 3 levels of nesting. Component A updates state and passes part of the state object as props to two instances of a child component B. B uses these props to render a switch component and 2 input components. Here's the simplified code for this hierarchy.
Component A:
const A: FC = () => {
// ....
// graphql queries to get data and update state using reducer
// ...
return (
<B
enabled={data.a.b.enabled}
value1={data.a.b.value1}
value2={data.a.b.value2}
/>
<B
enabled={data.a.b.enabled}
value1={data.a.b.value1}
value2={data.a.b.value2}
/>
);
};
Component B:
const B: FC = props => {
const { value1, value2, enabled} = props; // there are other props as well
return (
<>
<div className={someClassLogic}>
<Switch
onChange={onValueChange}
isChecked={enabled}
disabled={disabled}
/>
</div>
<div className={someClassLogic} >
<Input
input={value1}
disabled={disabled}
/>
</div>
<div className={someClassLogic}>
<Input
input={value2}
disabled={disabled}
/>
</div>
</>
);
};
A tablerow click event is used to update the state and the component B displays the 'settings' of this selected item which the user can mutate using the component B.
Here's the problem I'm facing- when the state is updated by a user action (selecting a row from a table, not shown in the snippet), I can see that both A and B receive the new data in the react developer tools and by printing to the console. But, a render does not take place to show the new data. I would like to understand why this is the case.
After looking up this issue, I figured I need a key prop while instantiating component B (the answers don't clearly explain why). With the following addition, the values did render correctly. Why is a key necessary here and why does it only work when the key contains all props that can change values? If I only use the uniqueId as the key, the value1 and value2 do not render correctly again. If I have many changing-props, do I have to them to add the key as well? Isn't there a less clumsy approach?
Updated Component A:
const A: FC = () => {
return (
<B
key={`${data.a.uniqueId}-
${data.a.b.value1}-
${data.a.b.value2}
enabled={data.a.b.enabled}
value1={data.a.b.value1}
value2={data.a.b.value2}
/>
<B
key={`${data.a.uniqueId}-
${data.a.b.value1}-
${data.a.b.value2}
enabled={data.a.b.enabled}
value1={data.a.b.value1}
value2={data.a.b.value2}
/>
);
};
Also, I noticed that although clicking on a table row rendered the correct value in component B now, but clicking on a row that is not changed by the user so far would cause the previously rendered values to remain on the Input1 and Input2 components (instead of blank). So I had to add keys to the Inputs as well with the enabled state attached to it which fixed this issue.
Updated Component B:
const B: FC = props => {
const { value1, value2, enabled} = props; // there are other props as well
return (
<>
<div className={someClassLogic}>
<Switch
onChange={onValueChange}
isChecked={enabled}
disabled={disabled}
/>
</div>
<div className={someClassLogic} >
<Input
key={`value1-${enabled}`}
input={value1}
disabled={disabled}
/>
</div>
<div className={someClassLogic}>
<Input
key={`value2-${enabled}`}
input={value2}
disabled={disabled}
/>
</div>
</>
);
};
Here again, why is a key needed? Doesn't react figure out that the props have changed and automatically render again?

Using Stateful React classes in typescipt

I am trying to create a Stateful class in which you can call methods such as createHeaderButton() where after calling it would update the state and re-render with these new updates in the component.
Im using Material-UI and so most of their styling utilizes Reacts hook API which of course classes cant use. Ive tried to get around this by using;
export default withStyles(useStyles)(HeaderBar)
Which exports the class separately with the Styles(withStyles(useStyles) useStyles as the defined styles) And the class(HeaderBar). Now the only issue is that i need to access the styles in my class. Ive found a JS example online that wont work for me because of the strong typed syntax of TS. Additionally When initializing my Class component in other places i try to get the ref=(ref:any)=>{} And with that call the create button methods when i get a response from my server, Which doesnt work because of this new way of exporting the class component!
Thanks for the help, Heres my component class: https://pastebin.pl/view/944070c7
And where i try to call it: https://pastebin.com/PVxhKFHJ
My personal opinion is that you should convert HeaderBar to a function component. The reason that it needs to be a class right now is so you can use a ref to call a class method to modify the buttons. But this is not a good design to begin with. Refs should be avoided in cases where you can use props instead. In this case, you can pass down the buttons as a prop. I think the cleanest way to pass them down is by using the special children prop.
Let's create a BarButton component to externalize the rendering of each button. This is basically your this.state.barButtons.forEach callback, but we are moving it outside of the HeaderBar component to keep our code flexible since the button doesn't depend on the HeaderBar (the header bar depends on the buttons).
What is a bar button and what does it need? It needs to have a label text and a callback function which we will call on click. I also allowed it to pass through any valid props of the material-ui Button component. Note that we could have used children instead of label and that's just down to personal preference.
You defined your ButtonState as a callback which takes the HTMLButtonElement as a prop, but none of the buttons shown here use this prop at all. But I did leave this be to keep your options open so that you have the possibility of using the button in the callback if you need it. Using e.currentTarget instead of e.target gets the right type for the element.
import Button, {ButtonProps as MaterialButtonProps} from "#material-ui/core/Button";
type ButtonState = (button: HTMLButtonElement) => void;
type BarButtonProps = {
label: string;
callback: ButtonState;
} & Omit<MaterialButtonProps, 'onClick'>
const BarButton = ({ label, callback, ...props }: BarButtonProps) => {
return (
<Button
color="inherit" // place first so it can be overwritten by props
onClick={(e) => callback(e.currentTarget)}
{...props}
>
{label}
</Button>
);
};
Our HeaderBar becomes a lot simpler. We need to render the home page button, and the rest of the buttons will come from props.childen. If we define the type of HeaderBar as FunctionComponent that includes children in the props (through a PropsWithChildren<T> type which you can also use directly).
Since it's now a function component, we can get the CSS classes from a material-ui hook.
const useStyles = makeStyles({
root: {
flexGrow: 1
},
menuButton: {
marginRight: 0
},
title: {
flexGrow: 1
}
});
const HeaderBar: FunctionComponent = ({ children }) => {
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<HeaderMenu classes={classes} />
<Typography variant="h6" className={classes.title}>
<BarButton
callback={() => renderModule(<HomePage />)}
style={{ color: "white" }}
label="Sundt Memes"
/>
</Typography>
{children}
</Toolbar>
</AppBar>
</div>
);
};
Nothing up to this point has used state at all, BarButton and HeaderBar are purely for rendering. But we do need to determine whether to display "Log In" or "Log Out" based on the current login state.
I had said in my comment that the buttons would need to be stateful in the Layout component, but in fact we can just use state to store an isLoggedIn boolean flag which we get from the response of AuthVerifier (this could be made into its own hook). We decide which buttons to show based on this isLoggedIn state.
I don't know what this handle prop is all about, so I haven't optimized this at all. If this is tied to renderModule, we could use a state in Layout to store the contents, and pass down a setContents method to be called by the buttons instead of renderModule.
interface LayoutProp {
handle: ReactElement<any, any>;
}
export default function Layout(props: LayoutProp) {
// use a state to respond to an asynchronous response from AuthVerifier
// could start with a third state of null or undefined when we haven't gotten a response yet
const [isLoggedIn, setIsLoggedIn] = useState(false);
// You might want to put this inside a useEffect but I'm not sure when this
// needs to be re-run. On every re-render or just once?
AuthVerifier.verifySession((res) => setIsLoggedIn(res._isAuthenticated));
return (
<div>
<HeaderBar>
{isLoggedIn ? (
<BarButton
label="Log Out"
callback={() => new CookieManager("session").setCookie("")}
/>
) : (
<>
<BarButton
label="Log In"
callback={() => renderModule(<LogInPage />)}
/>
<BarButton
label="Sign Up"
callback={() => renderModule(<SignUpPage />)}
/>
</>
)}
</HeaderBar>
{props.handle}
</div>
);
}
I believe that this rewrite will allow you to use the material-ui styles that you want as well as improving code style, but I haven't actually been able to test it since it relies on so many other pieces of your app. So let me know if you have issues.

How to use react-to-print with TypeScript?

I usually use react-to-print (https://www.npmjs.com/package/react-to-print) for printing React components with a low effort and great flexibility. I'm starting to write my applications with TypeScript, and this is the first time I need to combine these two things.
This is my code:
<ReactToPrint
trigger={() => <Button variant="contained" color="primary">Generar</Button>}
content={() => componentRef.current}
/>
<PrintableComponent ref={componentRef} />
To create the reference, I simply do:
const componentRef = useRef();
In JavaScript, it works, but when I use .tsx, I get an error in the "content" parameter of the ReactToPrint component and another in the ref parameter of my own PrintableComponent. Could someone help me with this?
Basically, the errors say that the interfaces do not match.
You can define the type for useRef to get rid of the ts error credit to shane935:
const componentRef = useRef<HTMLDivElement>(null)
And if you, like me, are using functional components you will get a problem trying to use that ref following react-to-print class based components. To bypass this error you can wrap your component you wish to print in a div:
<ReactToPrint
trigger={() => <Button variant="contained" color="primary">Generar</Button>}
content={() => componentRef.current}
/>
<div ref={componentRef}>
<PrintableComponent />
</div>
Everything inside this div will be printed.
Seems like a known issue when using hooks:
https://github.com/gregnb/react-to-print/issues/214
As an alternative, you can avoid the useRef hook and follow the example in the source repo which seems to work in TypeScript:
https://github.com/gregnb/react-to-print/blob/master/example/index.tsx
i.e., the first example on the npm readme doc:
https://www.npmjs.com/package/react-to-print#example

React click is not detected

I have a component where I want to listen to its click event by using an onClick attribute. Simple stuff. However, when I click the component the click event doesn't fire.
My component structure is as follows (I use styled-components, but that should not be related):
// this comes from my UI library
const Icon = styled.div`
/* some css properties */
`
const Search = () => (
<Icon>
/* this is an svg imported from the react-icons library */
<MdSearch />
</Icon>
)
// this is where I use the stuff from my UI library
class SomeComponent extends Component {
handleClick = () => {
// do something
}
render() {
return (
<div>
/* some other stuff */
<Search onClick={this.handleClick} />
</div>
)
}
}
The click is only detected when I spread the props down in the Search component, like this:
const Search = (props) => (
<Icon {...props}>
/* this is an svg imported from the react-icons library */
<MdSearch />
</Icon>
)
However, I am totally confused by this behaviour. Why can I not just make any component directly clickable? But instead have to manually pass the onClick prop down to the next DOM element? If that's just how it is, is there a more elegant solution than spreading the props? Because that would kind of mess up my whole UI library... :-)
The {...props} is required in this way:
<Icon {...props}>
/* this is an svg imported from the react-icons library */
<MdSearch />
</Icon>
so that the props you are passing in to Search (ie the onClick={this.handleClick}) actually get passed and attached to, a component inside of the (functional) component. Without the ...props, those props are passed in but are then not actually "attached" to anything, or used in anyway.
To not use the spread operator as shown above is roughly equivalent to creating a the following function:
foo(x) { return 1 }
and wondering why different values for x don't affect the behaviour/result of foo.
Hope that clarifies and helps :-)

Resources