Right now I'm working on a component that needs references to scroll to specific items. However when the component updates all my refs become null. Now I know this is actually intentional behavior because of memory leaks (etc.) but I don't care in this case.
Right now all my create ref code is being called in the constructor, but when I try and recreate the refs they're always null.
I generate the refs in the constructor of my component:
constructor(props) {
super(props);
this.eventRefs = props.events.map(() => React.createRef());
this.scrollRef = React.createRef();
this.mapRef = React.createRef();
}
When I try to look at those same refs later:
componentDidUpdate(prevProps) {
// Even if I recreate the refs here
// All those values set above are equal to { current: null }
}
Turns out the solution is to not generate the refs in the constructor or update them in componentDidUpdate but to create the references before rending:
render() {
this._createRefs();
return ( /* ... */ );
}
Related
class Myclass extends Component {
constructor(props) {
super(props);
this.onLoad();
}
onLoad() {
this.state.temp = 'Some value';
}
}
the state cannot be set in the constructor itself as it need to be set based on some conditions when onLoad() is called multiple times. And when the value is assigned component re rendering is also not required.
is assigning a value to state with out using setState good practice? if not, What would be cons?
this.state.x = 'something' is definitely not the right way to set the state. Also, even though you can do it, it is not recommended to perform side-effects in the constructor what so ever as it slows down the mounting times. Refer to: https://reactjs.org/docs/react-component.html#constructor
So I would recommend you to perform your side-effects in componentDidMount. You could do something as follows:
class Myclass extends Component {
constructor(props) {
super(props);
this.onLoad = this.onLoad.bind(this);
}
componentDidMount() {
this.onLoad();
}
onLoad() {
this.setState({ temp: 'Some value'});
}
}
Again, you can't use this.state.temp = 'some val' to change states in React because it doesn't re-render the component. You will always need to use setState to make sure your component re-renders. Refer to this link for more information.
Let me know if this has helped, and if not, I will happily answer your queries in comments below. :)
I don't know why you have to set in another function.
what's for?
you have to use setState to change states in not constructor.
anyway, you can use callback function of setState if you need to response right away after changing state.
setState(
{
temp: 'Some value'
},
() => console.log(this.state)
)
or just use variables inside the class.
I would like to call a method on a random component after creating it but before rendering it. To perform child specific calculations for the parent prior to rendering the parent. A simple static should work.
class Container extends ReactWrapper{
render() {
const rClass = React.createClass(this.getCCArgs());
var newData = rClass.expectedUtilityFunction(data);
// render parent with new data.
return (<div {...this.props.data, ...newData}>
{rClass}
</div>);
};
};
Tried a number of ways and the utility method is always not found.
I could push things up the line logically and add a method to return the class used to create the react instance from the input data but I already have the react class instance.
React.createClass is deprecated and removed from React 16+. So I suggest stop tring to make it work.
Your code is good fit for High order component.
Below is sample code (I didn't tested it, so use it as hint only)
function WrappedComponent (Component, props) {
var newData = /* Here is good place for expectedUtilityFunction code. Don't put expectedUtilityFunction function into Component, put it here, in HOC body */
// render parent with new data.
return (<div {...props.data, ...newData}>
<Component/>
</div>);
}
And use HOC like this
class Container extends ReactWrapper{
constructor(props) {
supre(props);
// Assuming that this.getCCArgs() returns component
this.hoc = WrappedComponent (this.getCCArgs(), props);
}
render() {
return this.hoc;
}
}
In my react project, the componentWillReceiveProps() function seems to be called twice, but not sure what the problem is.
Here is the code.
import React, { Component, PropTypes } from 'react';
...
class MessagesList extends Component {
constructor(props) {
super(props);
this.state = {
message: '',
messages: []
};
...
componentWillMount() {
this.props.init_message();
};
componentWillReceiveProps(nextProps) {
this.setState({
messages: this.props.user.messages
});
var msgs = this.props.user.messages;
var total_group = [];
var msg_group = [];
var group_date = 0;
setTimeout(() => {
if (typeof msgs != 'undefined') {
for(var i = 0; i < msgs.length; i++) {
...
}
}
}, 100);
};
render() {
return (
<div className="row">
<div className="col-12">
<div className="messages">
{this.state.messages.map(message => {
return (
<div>{message.user}: {message.message} : {message.date}</div>
)
})}
</div>
</div>
...
</div>
);
}
}
I was going to read the msgs.length in the componentWillReceiveProps(), I got the following issue.
msgs.length is undefiend
After that I got the values of array, so I think the componentWillReceiveProps() seems to be called twice. So in the first call, can't read the value and then in the second call, read the value at least.
Please help me.
componentWillReceiveProps is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that if a parent component causes your component to re-render, this method will be called even if props have not changed. Make sure to compare the current and next values if you only want to handle changes.
You will get the details from react docs.
ComponentWillReceiveProps is a method from the growth/update phase of the React lifecycle.
The Growth phase is triggered in three different ways: changing of props, changing of state or calling forceUpdate().
The value you are referring to in componentWillReceiveProps, this.props.user.messages, is the current value not the nextProps value.
Also something to consider is that the setState method is actually an asynchronous function. So when that setting of state takes place, it will cause another rerender.
I suspect, but I cannot be sure without more of your code, that setState is called once with your original value from props which triggers another update cycle. During this next update cycle the setState method now sets state to the new prop values.
Are you perhaps meaning to use nextProps.user.messages instead of this.props.user.messages?
I have a question. Perhaps a little silly.
For example i have class, with state and baseState, who linked to state inside construcor
class SomeStrangeClass extends Component {
constructor(props){
super(props);
this.state = {
confirmError:[],
};
this.baseState = this.state;
}
componentWillReceiveProps(nextProps){
console.log(this.state); //here i can have, for example, 3 confirmErrors...
console.log(this.baseState); // here i still have an empty array
this.setState(this.baseState);
}
...some strange code
}
Of course it is good for me. Simple reset of state)))
But..why this.basestate is empty, meanwhile this.state is not.
Why javascript behaves as if did not create a reference to the object?
Thank you very mutch!
With
this.baseState = this.state;
you are defining the completely new property of that React.Component class.
I the constructor means at the specific time when you initialize your React.Component object they will be equal.
It is a JavaScript feature to add new properties to any object dynamically.
Later on, when you execute setState() method of the Component class only the predefined state property will update and not the other one.
Here is the part of the source code for the React.Component :
/**
* Module for creating composite components.
*
* #class ReactClass
*/
var ReactClass = {
createClass: function(spec) {
// To keep our warnings more understandable, we'll use a little hack here to
// ensure that Constructor.name !== 'Constructor'. This makes sure we don't
// unnecessarily identify a class without displayName as 'Constructor'.
var Constructor = identity(function(props, context, updater) {
// This constructor gets overridden by mocks. The argument is used
// by mocks to assert on what gets mounted.
if (__DEV__) {
warning(
this instanceof Constructor,
'Something is calling a React component directly. Use a factory or ' +
'JSX instead. See: https://facebook.github.io/react/warnings/legacy-factories.html'
);
}
// Wire up auto-binding
if (this.__reactAutoBindPairs.length) {
bindAutoBindMethods(this);
}
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
this.state = null;
just to visualize what is inside the component.
When you do the assignment this.baseState = this.state, you are assigning the value of this.state at that time to the variable this.baseState, essentially making a copy. If this.state is updated with new variables after this, it does not reflect in the copy you made before.
I'm attempting to make a nice ApiWrapper component to populate data in various child components. From everything I've read, this should work: https://jsfiddle.net/vinniejames/m1mesp6z/1/
class ApiWrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
response: {
"title": 'nothing fetched yet'
}
};
}
componentDidMount() {
this._makeApiCall(this.props.endpoint);
}
_makeApiCall(endpoint) {
fetch(endpoint).then(function(response) {
this.setState({
response: response
});
}.bind(this))
}
render() {
return <Child data = {
this.state.response
}
/>;
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
data: props.data
};
}
render() {
console.log(this.state.data, 'new data');
return ( < span > {
this.state.data.title
} < /span>);
};
}
var element = < ApiWrapper endpoint = "https://jsonplaceholder.typicode.com/posts/1" / > ;
ReactDOM.render(
element,
document.getElementById('container')
);
But for some reason, it seems the child component is not updating when the parent state changes.
Am I missing something here?
There are two issues with your code.
Your child component's initial state is set from props.
this.state = {
data: props.data
};
Quoting from this SO Answer:
Passing the intial state to a component as a prop is an anti-pattern
because the getInitialState (in our case the constuctor) method is only called the first time the
component renders. Never more. Meaning that, if you re-render that
component passing a different value as a prop, the component
will not react accordingly, because the component will keep the state
from the first time it was rendered. It's very error prone.
So if you can't avoid such a situation the ideal solution is to use the method componentWillReceiveProps to listen for new props.
Adding the below code to your child component will solve your problem with Child component re-rendering.
componentWillReceiveProps(nextProps) {
this.setState({ data: nextProps.data });
}
The second issue is with the fetch.
_makeApiCall(endpoint) {
fetch(endpoint)
.then((response) => response.json()) // ----> you missed this part
.then((response) => this.setState({ response }));
}
And here is a working fiddle: https://jsfiddle.net/o8b04mLy/
If the above solution has still not solved your problem I'll suggest you see once how you're changing the state, if you're not returning a new object then sometimes react sees no difference in the new previous and the changed state, it's a good practice to always pass a new object when changing the state, seeing the new object react will definitely re-render all the components needing that have access to that changed state.
For example: -
Here I'll change one property of an array of objects in my state, look at how I spread all the data in a new object. Also, the code below might look a bit alien to you, it's a redux reducer function BUT don't worry it's just a method to change the state.
export const addItemToCart = (cartItems,cartItemToBeAdded) => {
return cartItems.map(item => {
if(item.id===existingItem.id){
++item.quantity;
}
// I can simply return item but instead I spread the item and return a new object
return {...item}
})
}
Just make sure you're changing the state with a new object, even if you make a minor change in the state just spread it in a new object and then return, this will trigger rendering in all the appropriate places.
Hope this helped. Let me know if I'm wrong somewhere :)
There are some things you need to change.
When fetch get the response, it is not a json.
I was looking for how can I get this json and I discovered this link.
By the other side, you need to think that constructor function is called only once.
So, you need to change the way that you retrieve the data in <Child> component.
Here, I left an example code: https://jsfiddle.net/emq1ztqj/
I hope that helps.
Accepted answer and componentWillReceiveProps
The componentWillReceiveProps call in accepted answer is deprecated and will be removed from React with version 17 React Docs: UNSAFE_componentWillReceiveProps()
Using derived state logic in React
As the React docs is pointing, using derived state (meaning: a component reflecting a change that is happened in its props) can make your components harder to think, and could be an anti-pattern. React Docs: You Probably Don't Need Derived State
Current solution: getDerivedStateFromProps
If you choose to use derived state, current solution is using getDerivedStateFromProps call as #DiogoSanto said.
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing. React Docs: static getDerivedStateFromProps()
How to use componentWillReceiveProps
This method can not access instance properties. All it does describing React how to compute new state from a given props. Whenever props are changed, React will call this method and will use the object returned by this method as the new state.
class Child extends React.Component {
constructor() {
super(props);
// nothing changed, assign the state for the
// first time to teach its initial shape.
// (it will work without this, but will throw
// a warning)
this.state = {
data: props.data
};
}
componentWillReceiveProps(props) {
// return the new state as object, do not call .setState()
return {
data: props.data
};
}
render() {
// nothing changed, will be called after
// componentWillReceiveProps returned the new state,
// each time props are updated.
return (
<span>{this.state.data.title}</span>
);
}
}
Caution
Re-rendering a component according to a change happened in parent component can be annoying for user because of losing the user input on that component.
Derived state logic can make components harder to understand, think on. Use wisely.