Passing props to makeStyles react - javascript

I have this component
const MyComponent = (props) => {
const classes = useStyles(props);
return (
<div
className={classes.divBackground}
backgroundImageLink={props.product?.image}
sx={{ position: "relative" }}
></div>
);
};
export default MyComponent;
I am trying to pass backgroundImage link in props and trying to put into makeStyles
export default makeStyles(props => ({
divBackground:{
background:`url("${props.backgroundImageLink}")`,
}
}));
But this does not works
& I am getting this warning in console
index.js:1 Warning: React does not recognize the `backgroundImage` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `backgroundimage` instead. If you accidentally passed it from a parent component, remove it from the DOM element.

You're not supposed to pass arbitrary attributes to the native elements (div in this case) because it doesn't do anything. The prop only works when passed in useStyles:
export default makeStyles({
divBackground: {
background: props => `url("${props.product?.image}")`,
}
});
Usage
const MyComponent = (props) => {
// you only need to pass the props here. useStyles will then use the
// prop.product.image to create the background property, generate a
// stylesheet and return the class name for you.
const classes = useStyles(props);
return (
<div
className={classes.divBackground}
// remove this line -----> backgroundImageLink={props.product?.image}
sx={{ position: "relative" }}
></div>
);
};

const MyComponent = (props) => {
const classes = useStyles(props)();
return (
<div
className={classes.divBackground}
backgroundImageLink={props.product?.image}
sx={{ position: "relative" }}
></div>
);
};
export default MyComponent;
then :
export default useStyles=(props)=>makeStyles(()=> ({
divBackground:{
background:`url("${props.backgroundImageLink}")`,
}
}));

Related

How to call the function and pass the parameter from import in React js? [duplicate]

I have a seemingly trivial question about props and function components. Basically, I have a container component which renders a Modal component upon state change which is triggered by user click on a button. The modal is a stateless function component that houses some input fields which need to connect to functions living inside the container component.
My question: How can I use the functions living inside the parent component to change state while the user is interacting with form fields inside the stateless Modal component? Am I passing down props incorrectly?
Container
export default class LookupForm extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false
};
}
render() {
let close = () => this.setState({ showModal: false });
return (
... // other JSX syntax
<CreateProfile fields={this.props} show={this.state.showModal} onHide={close} />
);
}
firstNameChange(e) {
Actions.firstNameChange(e.target.value);
}
};
Function (Modal) Component
const CreateProfile = ({ fields }) => {
console.log(fields);
return (
... // other JSX syntax
<Modal.Body>
<Panel>
<div className="entry-form">
<FormGroup>
<ControlLabel>First Name</ControlLabel>
<FormControl type="text"
onChange={fields.firstNameChange} placeholder="Jane"
/>
</FormGroup>
);
};
Example: say I want to call this.firstNameChange from within the Modal component. I guess the "destructuring" syntax of passing props to a function component has got me a bit confused. i.e:
const SomeComponent = ({ someProps }) = > { // ... };
You would need to pass down each prop individually for each function that you needed to call
<CreateProfile
onFirstNameChange={this.firstNameChange}
onHide={close}
show={this.state.showModal}
/>
and then in the CreateProfile component you can either do
const CreateProfile = ({onFirstNameChange, onHide, show }) => {...}
with destructuring it will assign the matching property names/values to the passed in variables. The names just have to match with the properties
or just do
const CreateProfile = (props) => {...}
and in each place call props.onHide or whatever prop you are trying to access.
I'm using react function component
In parent component first pass the props like below shown
import React, { useState } from 'react';
import './App.css';
import Todo from './components/Todo'
function App() {
const [todos, setTodos] = useState([
{
id: 1,
title: 'This is first list'
},
{
id: 2,
title: 'This is second list'
},
{
id: 3,
title: 'This is third list'
},
]);
return (
<div className="App">
<h1></h1>
<Todo todos={todos}/> //This is how i'm passing props in parent component
</div>
);
}
export default App;
Then use the props in child component like below shown
function Todo(props) {
return (
<div>
{props.todos.map(todo => { // using props in child component and looping
return (
<h1>{todo.title}</h1>
)
})}
</div>
);
}
An addition to the above answer.
If React complains about any of your passed props being undefined, then you will need to destructure those props with default values (common if passing functions, arrays or object literals) e.g.
const CreateProfile = ({
// defined as a default function
onFirstNameChange = f => f,
onHide,
// set default as `false` since it's the passed value
show = false
}) => {...}
just do this on source component
<MyDocument selectedQuestionData = {this.state.selectedQuestionAnswer} />
then do this on destination component
const MyDocument = (props) => (
console.log(props.selectedQuestionData)
);
A variation of finalfreq's answer
You can pass some props individually and all parent props if you really want (not recommended, but sometimes convenient)
<CreateProfile
{...this.props}
show={this.state.showModal}
/>
and then in the CreateProfile component you can just do
const CreateProfile = (props) => {
and destruct props individually
const {onFirstNameChange, onHide, show }=props;

How to add JSX inside props?

I have a component which has props value. This component is global and I don't want to change its source code.
I want to add pre tag inside the value prop to have a proper indentation between title and value.
<component
title = {<strong>Name</strong>}
value = {<pre> {name === undefined ? myName : myNames}</pre>}
/>
Issue: I get NaN for value prop.
How do I fix it?
Also, is there a way to add a gap/padding between title and value? Please note that I do not wish to modify the source file of Component.
Whenever I face a situation like this, my first approach is to make a helper function to render the smaller parts and pass them as props while invoking that function simultaneously.
For instance, for your case:-
//helper function to render title
const renderTitle = () => {
return <strong>Name</strong>;
};
//helper function to render value, you can also add styles to them via inline styles or using class or id.
const renderValue = () => {
return (
<pre style={{ marginLeft: 15 }}>
{name === undefined ? "myName" : "myNames"}
</pre>
);
};
return (
<div style={{ display: "flex" }}>
<CustomComponent title={renderTitle()} value={renderValue()} />
// invoking the helper functions to render the title and value
</div>
);
Codesandbox Link - https://codesandbox.io/s/eloquent-resonance-h4m49
You can pass prop Components in that following Example what I made for live view you can check here SandBox
import React, { useState } from 'react';
import { render } from 'react-dom';
const Success = () => {
return <pre>Your state isn't undefined</pre>
}
const Undefined = () => {
return <pre>Your state isn't undefined</pre>
}
const ReturnProp = ({propJSX}) => {
return propJSX
}
const App = () => {
const [name, setName] = useState(undefined)
return <ReturnProp propJSX={name===undefined? <Undefined />: <Success />}/>
}
render(<App />, document.querySelector('#app'));

Passing React State Between Imported Components

I am trying to pass state from parent to child using React, however both components are imported and therefor the state variables of the parent component are not declared.
I have two components both exported from the same file. The first component is a wrapper for the second. This component has a useEffect function which find its height and width and set these values to hook state.
export const TooltipWrapper = ({ children, ariaLabel, ...props }) => {
const [width, setWidth] = React.useState(0);
const [height, setHeight] = React.useState(0);
const ref = React.useRef(null);
React.useEffect(() => {
if (ref.current && ref.current.getBoundingClientRect().width) {
setWidth(ref.current.getBoundingClientRect().width);
}
if (ref.current && ref.current.getBoundingClientRect().height) {
setHeight(ref.current.getBoundingClientRect().height);
}
});
return <TooltipDiv>{children}</TooltipDiv>;
The next component which is exported from the same file looks like this
export const Tooltip = ({
ariaLabel,
icon,
iconDescription,
text,
modifiers,
wrapperWidth,
}) => {
return (
<TooltipContainer
aria-label={ariaLabel}
width={wrapperWidth}
>
<TooltipArrow data-testid="tooltip-arrow" modifiers={modifiers} />
<TooltipLabel
aria-label={ariaLabel}
>
{text}
</TooltipLabel>
</TooltipContainer>
);
};
The component Tooltip is expecting a prop wrapperWidth. This is where I want to pass in the width hook value from the TooltipWrapper component.
Both components are imported into my App component
import React from "react";
import { GlobalStyle } from "./pattern-library/utils";
import { Tooltip, TooltipWrapper } from "./pattern-library/components/";
function App() {
return (
<div className="App">
<div style={{ padding: "2rem", position: "relative" }}>
<TooltipWrapper>
<button style={{ position: "relative" }}>click </button>
<Tooltip
modifiers={["right"]}
text="changing width"
wrapperWidth={width}
/>
</TooltipWrapper>
</div>
</div>
);
}
Here I am told that width is not defined, which I expect since I'm not declaring width in this file.
Does anyone have an idea of how I can access the width and height state value for the parent component within the App file?
Render Props could work:
Add a renderTooltip prop to <TooltipWrapper>:
<TooltipWrapper renderTooltip={({ width }) => <Tooltip ...existing wrapperWidth={width} />}>
<button style={{ position: 'relative' }}>click</button>
</TooltipWrapper>
NB. ...existing is just the other props you are using with Tooltip
And then update the return of <TooltipWrapper>:
return (
<TooltipDiv>
{children}
props.renderTooltip({ width });
</TooltipDiv>
);

React HOC working on some but not other components

I'm using a HOC component to bind an action to many different types of element, including SVG cells, which, when an onClick is bound normally, it works, but when I use my HOC it returns un-intended results.
Minimally reproducible example: https://codesandbox.io/s/ecstatic-keldysh-3viw0
The HOC component:
export const withReport = Component => ({ children, ...props }) => {
console.log(Component); //this only prints for ListItem elements for some reason
const { dispatch } = useContext(DashboardContext);
const handleClick = () => {
console.log('clicked!'); //even this wont work on some.
const { report } = props;
if (typeof report === "undefined") return false;
dispatch({ type: SET_ACTIVE_REPORT, activeReport: report });
dispatch({ type: TOGGLE_REPORT });
};
return (
<Component onClick={handleClick} {...props}>
{children}
</Component>
);
};
Usage working:
const ListItemWIthReport = withReport(ListItem); //list item from react-mui
{items.map((item, key) => (
<ListItemWithReport report={item.report} key={key} button>
{/* listitem children*/}
</ListItemWithReport>
))}
Usage not working:
const BarWithReport = withReport(Bar); //Bar from recharts
{bars.map((bar, index) => (
<BarWithReport
report={bar.report}
key={index}
dataKey={bar.name}
fill={bar.fill}
/>
))}
The ListItem works 100% as anticipated, however, the bars will not render inside of the BarChart. Similarly, with a PieChart the Cells will actually render, with the correct sizes according to their values, however, props like "fill" do not appear to pass down.
Am I using the HOC incorrectly? I don't see an option other than HOC for the inside of Charts as many types of elements will be considered invalid HTML?
You might be dealing with components that have important static properties that need to be hoisted into the wrapped component or need to have ref forwarding implemented in order for their parent components to handle them. Getting these pieces in place is important, especially when wrapping components where you don't know their internals. That Bar component, for example, does have some static properties. Your HOC is making those disappear.
Here's how you can hoist these static members:
import hoistNonReactStatic from 'hoist-non-react-statics';
export const withReport = Component => {
const EnhancedComponent = props => {
const { dispatch } = useContext(DashboardContext);
const handleClick = () => {
const { report } = props;
if (typeof report === "undefined") return false;
dispatch({ type: SET_ACTIVE_REPORT, activeReport: report });
dispatch({ type: TOGGLE_REPORT });
};
return (
<Component onClick={handleClick} {...props}/>
);
};
hoistNonReactStatic(EnhancedComponent, Component);
return EnhancedComponent;
};
Docs on hoisting statics and ref forwarding can be found in this handy guide to HOCs.
There may be some libraries that can take care of all these details for you. One, addhoc, works like this:
import addHOC from 'addhoc';
export const withReport = addHOC(render => {
const { dispatch } = useContext(DashboardContext);
const handleClick = () => {
const { report } = props;
if (typeof report === "undefined") return false;
dispatch({ type: SET_ACTIVE_REPORT, activeReport: report });
dispatch({ type: TOGGLE_REPORT });
};
return render({ onClick: handleClick });
});
Of course, if the parent component is checking child components by type explicitly, then you won't be able to use HOCs at all. In fact, it looks like recharts has that issue. Here you can see the chart is defined in terms of child components which are then searched for explicitly by type.
I think your HOC is invalid, because not every wrapper-Component (e.g. HTML element) is basically clickable. Maybe this snipped can clarify what I am trying to say:
const withReport = Component => (props) => {
const handleClick = () => console.log('whatever')
// Careful - your component might not support onClick by default
return <Component onClick={handleClick} {...props} />
// vs.
return <div onClick={handleClick} style={{backgroundColor: 'green'}}>
<Component {...props} />
{props.children}
</div>
}
// Your import from wherever you want
class SomeClass extends React.Component {
render() {
return <span onClick={this.props.onClick}>{this.props.children}</span>
// vs.
return <span style={{backgroundColor: 'red'}}>
{
// Careful - your imported component might not support children by default
this.props.children
}
</span>
}
}
const ReportedListItem = withReport(SomeClass)
ReactDOM.render(<ReportedListItem>
<h2>child</h2>
</ReportedListItem>, mountNode)
You can have the uppers or the unders (separated by vs.) but not crossed. The HOC using the second return (controlled wrapper-Component) is sure more save.
I've used 4 methods successfully to wrap Recharts components.
First Method
Wrap the component in a HOC and use Object.Assign with some overloads. This breaks some animation and difficult to use an active Dot on lines. Recharts grabs some props from components before rendering them. So if the prop isn't passed into the HOC, then it won't render properly.
...
function LineWrapper({
dataOverload,
data,
children,
strokeWidth,
strokeWidthOverload,
isAnimationActive,
dot,
dotOverload,
activeDot,
activeDotOverload,
...rest
}: PropsWithChildren<Props>) {
const defaultDotStroke = 12;
return (
<Line
aria-label="chart-line"
isAnimationActive={false}
strokeWidth={strokeWidthOverload ?? 2}
data={dataOverload?.chartData ?? data}
dot={dotOverload ?? { strokeWidth: defaultDotStroke }}
activeDot={activeDotOverload ?? { strokeWidth: defaultDotStroke + 2 }}
{...rest}
>
{children}
</Line>
);
}
export default renderChartWrapper(Line, LineWrapper, {
activeDot: <Dot r={14} />,
});
const renderChartWrapper = <P extends BP, BP = {}>(
component: React.ComponentType<BP>,
wrapperFC: React.FC<P>,
defaultProps?: Partial<P>
): React.FC<P> => {
Object.assign(wrapperFC, component);
if (defaultProps) {
wrapperFC.defaultProps = wrapperFC.defaultProps ?? {};
Object.assign(wrapperFC.defaultProps, defaultProps);
}
return wrapperFC;
};
Second Method
Use default props to assign values. Any props passed into the HOC will be overridden.
import { XAxisProps } from 'recharts';
import { createStyles } from '#material-ui/core';
import { themeExtensions } from '../../../assets/theme';
const useStyles = createStyles({
tickStyle: {
...themeExtensions.font.graphAxis,
},
});
type Props = XAxisProps;
// There is no actual implementation of XAxis. Recharts render function grabs the props only.
function XAxisWrapper(props: Props) {
return null;
}
XAxisWrapper.displayName = 'XAxis';
XAxisWrapper.defaultProps = {
allowDecimals: true,
hide: false,
orientation: 'bottom',
width: 0,
height: 30,
mirror: false,
xAxisId: 0,
type: 'category',
domain: [0, 'auto'],
padding: { left: 0, right: 0 },
allowDataOverflow: false,
scale: 'auto',
reversed: false,
allowDuplicatedCategory: false,
tick: { style: useStyles.tickStyle },
tickCount: 5,
tickLine: false,
dataKey: 'key',
};
export default XAxisWrapper;
Third Method
I didn't like this so I've worked around it, but you can extend the class.
export default class LineWrapper extends Line {
render(){
return (<Line {...this.props} />
}
}
Fourth Method
I don't have a quick example of this, but I always render the shape or children and provide functions to help. For example, for bar cells I use this:
export default function renderBarCellPattern(cellOptions: CellRenderOptions) {
const { data, fill, match, pattern } = cellOptions;
const id = _uniqueId();
const cells = data.map((d) =>
match(d) ? (
<Cell
key={`cell-${id}`}
strokeWidth={4}
stroke={fill}
fill={`url(#bar-mask-pattern-${id})`}
/>
) : (
<Cell key={`cell-${id}`} strokeWidth={2} fill={fill} />
)
);
return !pattern
? cells
: cells.concat(
<CloneElement<MaskProps>
key={`pattern-${id}`}
element={pattern}
id={`bar-mask-pattern-${id}`}
fill={fill}
/>
);
}
// and
<Bar {...requiredProps}>
{renderBarCellPattern(...cell details)}
</Bar>
CloneElement is just a personal wrapper for Reacts cloneElement().

How to access a public method from a wrapped Component using TypeScript?

I am working on a React project using TypeScript. They wrapped the react-select component into another component. The wrapped component is the following:
import * as React from "react";
import Select from "react-select";
import { Props as SelectProps } from "react-select/lib/Select";
export interface SelectValue {
label: string;
value: string;
}
export interface SelectFieldProps<TValue> extends SelectProps<TValue> {
label?: string;
}
type GenericSelectField<TValue> = React.StatelessComponent<
SelectFieldProps<TValue>
>;
const SelectField: GenericSelectField<SelectValue> = ({
label = "",
...rest
}) => (
<div className="react-select-wrapper">
{label ? <span className="input__label">{label}</span> : null}
<Select classNamePrefix="react-select" {...rest} />
</div>
);
export default SelectField;
I would like to access the method blur from react-select:
React-select exposes two public methods:
...
blur() - blur the control programatically
But, I don't know how to expose it in my Component, so I could invoke it. Any ideas?
You can use ref property on the Select component to get a reference to the instance of this component.
Then you can call the blur method of this instance.
const SelectField: GenericSelectField<SelectValue> = ({label = "",...rest}) => {
const selectEl = React.useRef(null);
// this function could be a callback
function handleBlur() {
selectEl.current.blur();
}
return (
<div className="react-select-wrapper">
{label ? <span className="input__label">{label}</span> : null}
<Select ref={selectEl} classNamePrefix="react-select" {...rest} />
</div>
);
}
export default SelectField;
If you need to access the blur method outside of your SelectField component, you could use forwardRef to reference Select instead of the SelectField.
const SelectField = (props, ref) => {
const {label = "",...rest} = props;
const selectEl = React.useRef(null);
return (
<div className="react-select-wrapper">
{label ? <span className="input__label">{label}</span> : null}
<Select ref={ref} classNamePrefix="react-select" {...rest} />
</div>
);
}
export default React.forwardRef(SelectField);
Then you can call the blur method with the reference of the SelectField component :
const ref = React.createRef();
<SelectField ref={ref} label=""/>;
ref.blur();
If I understand this correctly you want to trigger Select's blur() inside of SelectField.
There are many ways for example parent-child-binding it via props.
Here is a discussion about this:
Call child method from parent
If you want to access it from outside the SelectField component, I would recommend using React.forwardRef to forward the ref prop to your SelectField component so you can assign it on your own:
/* ... */
type GenericSelectField<TValue> = React.StatelessComponent<
SelectFieldProps<TValue>
>;
type SelectFieldWithRef<TValue> = React.StatelessComponent<
SelectFieldProps<TValue>,
React.Ref<*>
>;
const RefSelectField: SelectFieldWithRef<SelectValue> = (props, ref: any) => {
/* ... */
return (<Select ref={ref} {...} />);
};
const SelectField: GenericSelectField<SelectValue> = React.forwardRef(RefSelectField);
export default SelectField;
In the component you want to call blur you have to do the following:
class ParentComponent extends Component {
constructor(props) {
super(props);
this.selectRef = React.createRef();
}
somehowTriggerBlur = () => this.selectRef.current.blur();
render() {
return (<SelectField ref={ref} {...} />);
}
}

Categories

Resources