Parent Component Code
Image shows the parent class passing function and object as props
Export const CaseCard = ({
image, title, industry, geography, link, showModal, getFormId,
}) => {
const handleClick = () => {
showModal();
};
return (
When running the code it shows error i.e., showModal(); is not a function.
I think I'm doing destructuring the wrong way.
Any suggestions???
This should work:
export const CaseCard = (props) => {
const handleClick = () => {
props.showModal();
};
}
If you want to explicitly define which props should be passed, see the Typechecking With PropTypes guide or use TypeScript.
The approach was right, but I was placing modal inside Card Component. The solution is to place modal in the near parent component of Card.
Related
Hi developers I'm just a beginner in React.js. I tried to print props by passing from parent to child.
This is app.js file
import React from "react";
import Hooks from "./components/ReactHooks1";
import Hooks2 from "./components/ReactHooks2";
const App = () => {
return (
<div>
<h1>
Welcome to React App
</h1>
<Hooks2 title2={"Welcome"}/>
</div>
)
}
export default App
This is child component file
import React from 'react';
const Hooks2 = (props) => {
console.log(props);
}
export default Hooks2;
I just try to print props but it shows an empty object. what am I doing wrong please help me on this
You should return something or null to parent component from child, when you're using it in parent component. This will solve your problem
export const Hooks2 = (props) => {
console.log(props);
return <></>;
}
#Rasith
Not sure why would you want to do this, but if you're trying to pass a child component that would print something to the console. In this case you need to destructure the component's props. Here's an article about it from MDN.
This is how I would do it:
const CustomComponent = ({title}) => {
console.log(title)
}
const App = () => {
return (
<>
<h1>Hello World</h1>
<CustomComponent title={"Welcome"}/>
</>
);
};
For the title to be printed to the console, no need to add a return statement to the child component. Again, not sure why you would do this, but there you go.
Well trying to console.log title certainly would not work because what you are passing is called title2. Also your child component is not returning anything.
First, you have to return anything from your child component( even a fragment )
You can access title2 in the child component with any of these methods:
1- using props object itself
const Hooks2 = (props) => {
console.log(props.title2);
return;
}
2- you can also destructure props in place to access title2 directly
const Hooks2 = ({title2}) => {
console.log(title2);
return ;
}
You have to use destructuring in your ChildComponent, to grab your props directly by name:
const Hooks2 = ({title2}) => {
console.log(title2);
}
You can read a little bit more about it in here: https://www.amitmerchant.com/always-destructure-your-component-props-in-react/
I was wondering if i could wrap dispatch action into function (for example class method). I have this component:
function Product({id}) {
const {state, dispatch} = React.useContext(CartContext);
return (
<button onClick={() => dispatch({type: "remove", payload: id})}>Remove</button>
)
}
What i want to achieve is to replace ugly looking dispatch call into more clear function like this:
<button onClick={() => Cart.remove(id))}>Remove</button>
Is it possible? I've tried by this way but hooks can't be called outside React component.
export default Cart {
static remove = id => React.useContext(CartContext).dispatch({type: "remove", payload: id});
}
What you need is to create a custom hook
const useRemoveCart = () => {
const {state, dispatch} = React.useContext(CartContext);
return id => dispatch({type: "remove", payload: id})
}
And now you can use this hook and call the return of it.
function Product({id}) {
const remove = useRemoveCart()
return (
<button onClick={() => remove(id)}>Remove</button>
)
}
But I don't feel like this is the way to go.
Probably the max thing you could do is create a useCart hook that will return state and dispatch. Creating a custom hook only for one function isn't good, because if you need another function, you will have to do a lot of refactor or create a new hook, and you will have one hook for each function, which will be very bad.
If I was you, I would do this
const useCart = () => React.useContext(CartContext)
Now you don't need to import useContext and CartContext, only import useCart
And probably create variables instead of passing the hole string "remove" which can cause some typos.
const REMOVE_CART = 'remove'
And use it like
dispatch({type: REMOVE_CART, payload: id})
Now you will never have a typo in the 'remove' string because if you do, it will give you an error.
You shouldn't pass dispatch to child components. Child components should typically pass the data back up to the parent, and the parent should solely be responsible for the state in the case. I'd suggest a Helper function in Product that does this.
I have some heavy forms that I'm dealing with. Thus, I'm trying to squeeze performance wherever I can find it. Recently I added the Why-did-you-render addon to get more insight on what might be slowing down my pages. I noticed that, for example, when I click on a checkbox component about all of my other components re-render. The justification is always the same. WDYR says
Re-rendered because of props changes: different functions with the
same name {prev onChangeHandler: ƒ} "!==" {next onChangeHandler: ƒ}
As much as possible, I try to respect best the best practices indications that I find. The callback functions that my component passes follow this pattern
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
export function TopLevelComponent({props}){
const defaultData = {name: '', useMale: false, useFemale: false}
const [data, setData] = useState(defData);
const { t } = useTranslation();
const updateState = (_attr, _val) => {
const update = {};
update[_attr] = _val;
setData({ ...data, ...update });
}
const updateName = (_v) => updateState('name', _v);//Text input
const updateUseMale = (_v) => updateState('useMale', _v);//checkbox
const updateUseFemale = (_v) => updateState('useFemale', _v);//checkbox
...
return <div>
...
<SomeInputComponent value={data.name} text={t('fullName')} onChangeHandler={updateName} />
<SomeCheckboxComponent value={data.useMale} onChangeHandler={updateUseMale} text={t('useMale')}/>
<SomeCheckboxComponent value={data.useFemale} onChangeHandler={updateUseFemale} text={t('useFemale')}/>
...
</div>
}
In an example like this one, altering any of the inputs (eg: Writing text in the text input or clicking one of the checkboxes) would cause the other 2 components to re-render with the justification presented above.
I guess that I could stop using functional components and utilize the shouldComponentUpdate() function, but functional components do present some advantages that I'd rather keep. How should I write my functions in such a way that interacting with one input does not force an update on another input?
The problem stems from the way you define your change handlers:
const updateName = (_v) => updateState('name', _v)
This line is called on each render and thus, every time your component is rendered, the prop has a new (albeit functionality-wise identical) value. The same holds for every other handler as well.
As an easy solution you can either upgrade your functional component to a fully fledged component and cache the handlers outside of the render function, or you can implement shouldComponentUpdate() in your child components.
You need to use memo for your child components to reduce renders
const SomeInputComponent = props => {
};
export default memo(SomeInputComponent);
// if it still causes rerender witout any prop change then you can use callback to allow or block render
e.f.
function arePropsEqual(prevProps, nextProps) {
return prevProps.name === nextProps.name; // use your logic to determine if props are same or not
}
export default memo(SomeInputComponent, arePropsEqual);
/* One reason for re-render is that `onChange` callback passed to child components is new on each parent render which causes child components to re-render even if you use `momo` because function is updated on each render so in order to fix this, you can use React hook `useCallback` to get the same function reference on each render.
So in you parent component, you need to do something like
*/
import { useCallback } from 'react';
const updateName = useCallback((_v) => updateState('name', _v), [])
You have to memoize parent function before pass to children, using useCallback for functional component or converting to class property if you use class.
export default class Parent extends React.PureComponent {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
console.log("click");
}
render() {
return (
<ChildComponent
onClick={ this.onClick }
/>
);
}
}
with useCallback:
Parent = () => {
const onClick = useCallback(
() => console.log('click'),
[]
);
return (
<ChildComponent
onClick={onClick}
/>
);
}
Working with an array of mapped items, I am attempting to toggle class in a child component, but state change in the parent component is not passed down to the child component.
I've tried a couple different approaches (using {this.personSelectedHandler} vs. {() => {this.personSelectedHandler()} in the clicked attribute, but neither toggled class successfully. The only class toggling I'm able to do affects ALL array items rendered on the page, so there's clearly something wrong with my binding.
People.js
import React, { Component } from 'react';
import Strapi from 'strapi-sdk-javascript/build/main';
import Person from '../../components/Person/Person';
import classes from './People.module.scss';
const strapi = new Strapi('http://localhost:1337');
class People extends Component {
state = {
associates: [],
show: false
};
async componentDidMount() {
try {
const associates = await strapi.getEntries('associates');
this.setState({ associates });
}
catch (err) {
console.log(err);
}
}
personSelectedHandler = () => {
const currentState = this.state.show;
this.setState({
show: !currentState
});
};
render() {
return (
<div className={classes.People}>
{this.state.associates.map(associate => (
<Person
name={associate.name}
key={associate.id}
clicked={() => this.personSelectedHandler()} />
))}
</div>
);
}
}
export default People;
Person.js
import React from 'react';
import classes from './Person.module.scss';
const baseUrl = 'http://localhost:1337';
const person = (props) => {
let attachedClasses = [classes.Person];
if (props.show) attachedClasses = [classes.Person, classes.Active];
return (
<div className={attachedClasses.join(' ')} onClick={props.clicked}>
<img src={baseUrl + props.photo.url} alt={props.photo.name} />
<p>{props.name}</p>
</div>
);
};
export default person;
(Using React 16.5.0)
First of all, in your People.js component, change your person component to:
<Person
name={associate.name}
key={associate.id}
clicked={this.personSelectedHandler}
show={this.state.show}}/>
You were not passing the prop show and also referring to a method inside the parent class is done this way. What #Shawn suggested, because of which all classes were toggled is happening because of Event bubbling.
In your child component Person.js, if you change your onClick to :
onClick={() => props.clicked()}
The parenthesis after props.clicked executes the function there. So, in your personSelectedHandler function, you either have to use event.preventDefault() in which case, you also have to pass event like this:
onClick={(event) => props.clicked}
and that should solve all your problems.
Here's a minimal sandbox for this solution:
CodeSandBox.io
I'm trying to write functional stateless components and in doing so have run into an issue. I wrap by Navigator in a function that takes props as an argument (these are arbitrary). The component that gets wrapped is currently defined as follows since it is used in the reducer for this component.
export const Navigator = StackNavigator(screens, navigatorConfig);
const NavigatorView = (props) => {
return (<Navigator screenProps={{ ...props }}/>);
};
This works, but I'd ideally like to be able to instantiate the Navigator as a local variable in the NavigatorView function, and export there so that I have flexibility towards what attributes I can set at instantiation time (like shown below).
const NavigatorView = (myArg, props) => {
const Navigator = StackNavigator(screens, myArg);
return (<Navigator screenProps={{ ...props }}/>);
};
The problem here is as follows, how can I export Navigator (that's used in the reducer) while keeping it wrapped in a function? I've tried module.exports = Navigator;, export const Navigator... but they do not work. Thanks.
How about making a function that returns a function?
export const generateNavigator = (screens, navigatorConfig) =>
StackNavigator(screens, navigatorConfig);
const NavigatorView = (myArg, props) => {
const Navigator = generateNavigator(screens, myArg);
return (<Navigator screenProps={{ ...props }}/>);
};