Im trying to play around with ReactCssTransition but somehow the event is not called(componentWillLeave)
Here's my component
import React, { Component } from 'react'
import TransitionGroup from 'react-addons-css-transition-group'
export default class TransitionComponent extends Component {
constructor(props) {
super(props);
}
render() {
return (
let {componentKey} = this.props
<TransitionGroup transitionName="example" transitionEnterTimeout={500} transitionLeaveTimeout={500}>
<Item key={"item"+componentKey} />
</TransitionGroup>
);
}
}
And the child component
class Item extends Component {
componentDidEnter() {
console.log("component did enter");
}
componentWillLeave(callback) {
console.log("component will leave");
}
render() {
return (
<div>Item</div>
)
}
}
Any clue? Thanks!
I had similar issue caused by not calling the callback within the "componentWillEnter" function. Quote from React documentation
It will block other animations from occurring until callback is called
class Item extends Component {
componentWillEnter(callback) {
console.log("component will enter");
callback();
}
componentDidEnter() {
console.log("component did enter");
}
componentWillLeave(callback) {
console.log("component will leave");
callback();
}
render() {
return (
<div>Item</div>
)
}
}
Please excuse me as I am new to React myself but my understanding is that when components are mounted/unmounted you will not get the componentDidEnter() and componentWillLeave() lifecycle hooks.
All you have at your disposal is componentDidMount() and componentWillUnmount().
So replace the above portion with this and you should see your console logs:
componentDidMount() {
console.log('Component did mount');
}
componentWillUnmount() {
console.log('Component will unmount');
}
I believe if you want access to all of the lifecycle hooks you need to use ReactTransitionGroup instead, I have not yet figured out how to implement this though.
There is a good article on it on Medium though where the author explains it quite well.
There is a bit in the article where she talks about wrapping her child component with the ReactTransitionGroup component and I believe that is what you are looking for. Basically wrapping it around your current <Item />...
Related
Is there any side effect I do not see by doing this ?
class App extends React.Component {
hello() {
console.log("hello")
}
render() {
return <Layout app={this}>
}
}
So later on I can refer to this.props.app.hello (and others) from Layout ?
This is not safe.
React will not know how to watch for changes, so you may miss re-renders. React uses === to check for state changes, and App will always be === to App, even when state or properties change.
Take this example:
class App extends React.Component {
constructor(props) {
super(props);
this.setState({text: 'default value'});
}
hello() {
this.setState({...this.state, text: 'new value'});
}
render() {
return (
<div onClick={this.hello}>
<Layout app={this}>
</div>
);
}
}
class Layout extends React.Component {
render() {
return <div>{this.app.state.text}</div>
}
}
When you click on the parent div, this.hello will be called, but the child component will not detect the state update, and may not re-render as expected. If it does re-render, it will be because the parent did. Relying on this will cause future bugs.
A safer pattern is to pass only what is needed into props:
class App extends React.Component {
//...
render() {
return (
<div onClick={this.hello}>
<Layout text={this.state.text}>
</div>
);
}
}
class Layout extends React.Component {
render() {
return <div>{this.props.text}</div>
}
}
This will update as expected.
Answer
There's nothing wrong in passing functions as props, as I can see in your example, the only thing you have to do is make sure your function is bound to the current component like the following example
Reference
React: Passing Functions to Components
Simple example but not sure why I can't get it to work:
class Grid extends React.Component {
componentDidUpdate = () =>{
alert('Should Fire')
}
render() {
return (<div>{this.props.name}</div>)
}
}
class Application extends React.Component {
render() {
return (<div><Grid name="test-grid" /></div>);
}
}
The componentDidUpdate method does not fire in the Grid class. Insight?
Instead you can use componentDidMount(). You are not updating the props which should trigger update event. See from the documentation of componentDidUpdate():
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
As the following:
class Grid extends React.Component {
componentDidMount = () =>{
alert('Should Fire')
}
render() {
return (<div>{this.props.name}</div>)
}
}
I hope this helps!
componentDidUpdate function only fires when the parent component re-renders causing the child to re-render.
In your case the parent isn't re-rendering at all. Its just the initial render.
To detect an initial render, you would make use of componentDidMount function. You can see the componentDidUpdate function being fired if you have a state in parent that you update based on an interaction or any other way
class Grid extends React.Component {
componentDidMount(){
alert('Should Fire')
}
componentDidUpdate() {
alert('ComponentDidUpdate Should Fire');
}
render() {
return (<div>{this.props.name}</div>)
}
}
class Application extends React.Component {
state = {
count: 0
}
render() {
return (
<div>
<Grid name="test-grid" />
<button onClick={()=>this.setState(prev => ({count: prev.count + 1}))}> increment</button>
</div>
);
}
}
ReactDOM.render(<Application />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"/>
I have this React component
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: [],
};
}
componentDidMount() {
// get the resources from the Link props and save it into the state
this.setState({
resources: this.props.location.resources,
});
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
It gets the resources from the Link component, and that works fine. If I check out the state of the Component from the dev tools, the state looks right. And I thought with my logic this should work. So firstly, the state is empty, the component gets rendered, since the state is empty it doesn't render any components. Then, setState gets called, it gets all the resources and saves them into the state, and then the component would re-render, and it should work, but it doesn't. I'm getting a TypeError: Cannot read property 'map' of undefined error. What is the correct way to do this and how do I fix this?
Try this code:
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: this.props && this.props.location && this.props.location.resources?this.props.location.resources:[],
};
}
componentDidMount() {
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
Or use directly props
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{
this.props && this.props.location &&
this.props.location.resources
?this.props.location.resources.map(res => (
<div>test</div>
))
:null
}
</div>
);
}
}
Or use componentWillReceiveProps or getDerivedStateFromProps life cycle methods.
Check this.props.location.resources is array.
See more: https://hackernoon.com/replacing-componentwillreceiveprops-with-getderivedstatefromprops-c3956f7ce607
For first check is this.props.location.resources array, or if data type changes you can add checking, you can use lodash isArray or with js like this:
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: [],
};
}
componentDidMount() {
// get the resources from the Link props and save it into the state
Array.isArray(this.props.location.resources) {
this.setState({
resources: this.props.location.resources,
});
}
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
Or you can just use hooks like this:
import React, { useState, useEffect } from "react";
export default function ResourceForField({location}) {
const [ resources, setResources ] = useState([]);
useEffect(() => {
if (location && Array.isArray(location.resources)) {
setResources(location.resources)
}
}, [location]);
return (
<div>
{resources.map(res => (
<div>test</div>
))}
</div>
);
}
If the internal state of ResourceForField doesn't change and always equals to its prop, you shouldn't save the prop in the state. You can instead create a pure functional component.
Also note that there's nothing preventing you from initializing the state from the props in constructor method. i.e. you're not required to wait for the component to mount in order to access the props.
So, I'd write the following component for ResourceForField:
function ResourceForField({resources = []}) {
return (
<div>
{
resources.map(res => (<div>test</div>))
}
</div>
);
}
My parent component
import EditReview from './partials/editReview'
class VenueDetails extends Component {
constructor(props) {
super(props)
this.child = React.createRef();
}
render() {
return (
<div className="place-review-text">
<EditReview {...this.props}/>
</div>
)
}
}
My child component
class EditReview extends Component {
onEditClick(review, editIndex) {
console.log('ppp')
}
render() {
const { handleSubmit, user, pristine, index, commentCrossClick } = this.props
return (
<div>
<Field
name="content"
component={renderTextArea}
className="form-control"
label="Write your review..."
rows={2}
/>
</div>
)
}
}
export default EditReview
I need to call onEditClick from the parent component. I tried this but doesn't work.
Kindly help me
Edit
After upgrade I am getting this
Error in ./~/react-dom/lib/ReactServerRendering.js
Module not found: 'react/lib/React' in /home/user/ashish/LTC/lovethesecities-frontend/node_modules/react-dom/lib
After resolving all the errors call child function from parent in react 16
React docs have a example of this using refs
https://reactjs.org/docs/refs-and-the-dom.html
I’m also wondering the use case of wanting to do this, maybe some context could help with an answer?
Try doing it like this:
import EditReview from './partials/editReview'
class VenueDetails extends Component {
render() {
return (
<div className="place-review-text">
<EditReview ref={Ref => this.child=Ref } {...this.props} />
</div>
)
}
}
and call the function in parent component as this.child.onEditClick(param1,param2)
EDIT1:
if you have to do it with react 15.x itself what you can do it is create the function in parent and pass it as a prop to child
I'm working on a react based app and I am checking user's permission before rendering some piece of jsx using a HasRole component. Everything works fine except if I have a function as a child of the HasRole component, the function is getting called every time the parent component renders, if the user doesn't have permission also. How do I prevent this from happening.
HasRole.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
class HasRole extends Component {
render() {
const { children, role, requiredRole } = this.props;
if(!requiredRole.includes(role)) return null;
return children;
}
}
const getMapStateToProps = (extendWith = {}) => state => {
return {
role: state.auth.userType,
...extendWith
}
}
export default connect(getMapStateToProps)(HasRole);
And when I use the HasRole component to check permission, {this.renderUsers} is executed if user don't have permission also.
MovieShow.js
import React, { Component } from 'react';
import HasRole from '../has-role';
class MovieShow extends Component{
constructor(props){
super(props);
}
renderUsers(){
...
}
render(){
return(
...
<HasRole requireRole={["admin"]}>
{this.renderUsers} // this will run if user is not admin also but the component wont be shown
</HasRole>
}
}
Thanks in advance!
Make the rendersUsers a stateless functional component by dragging it out of the class instead.
https://reactjs.org/docs/components-and-props.html
By doing this it should enable you to pass the requireRole param to it, and lock it behind permissions aswell.
The reason that renderUsers is executed is because it is evaluated in Parent although rendered as children in HasRole. You could write your HasRole component to use used with renderProps like
class HasRole extends Component {
render() {
const { children, role, requiredRole } = this.props;
if(!requiredRole.includes(role)) return null;
return children();
}
}
and then you would use it like
class MovieShow extends Component{
constructor(props){
super(props);
}
renderUsers(){
...
}
render(){
return(
...
<HasRole requireRole={["admin"]}>
{() => {return this.renderUsers()}}
</HasRole>
}
}