What does "Stateless function components cannot be given refs" mean? - javascript

I have this:
const ProjectsSummaryLayout = ({projects}) => {
return (
<div className="projects-summary col-md-10">
<h3>Projects</h3>
<ul>
{ projects.map(p => <li key={p.id}>{p.contract.client}</li>) }
</ul>
</div>
)
}
const ProjectsSummary = connect(
state => ({projects: state.projects})
)(ProjectsSummaryLayout)
and I get:
Warning: Stateless function components cannot be given refs (See ref
"wrappedInstance" in ProjectsSummaryLayout created by
Connect(ProjectsSummaryLayout)). Attempts to access this ref will
fail.
What is it trying to tell me? Am I actually doing something wrong?
I see discussion about this here but unfortunately I simply don't understand the conclusion.

In React, refs may not be attached to a stateless component.
React Redux 3 attaches a ref to the component you give it regardless of whether or not it's stateless. The warning you see comes from React because internally, React Redux 3 attaches a ref to the stateless component you supplied (ProjectsSummaryLayout).
You're not doing anything wrong and according to this GitHub comment, you can safely ignore the warning.
In React Redux 4, no ref is attached to the wrapped component by default, which means if you upgrade to React Redux 4, the warning should go away.

React has 2 commonly used component styles.
Functional Component
Class Component
So, When I was making use of Functional one then I was encountering this error.
Code snippet corresponding to Functional Component
But as soon as I changed it to Class Component then it worked.
Code snippet corresponding to Class Component.
Try changing Functional Component to Class Component.
I hope it will resolve your problem.

Related

How to props through the deeply nested child to parent data ? React

To explain the situation. I have a child component that has one chevron that spins on a click. I need to implement from child to parent and to grand parent props data.
<ChildComponent>
<ParentComponent>
<GrandParentComponent>
Inside ChildComponent I have arrow icon ( img )
const [rotateIcon, setRotateIcon] = useState(false);
const expandAttachment = useCallback(() => {
setRotateIcon(!rotateIcon);
}, [rotateIcon]);
<img src="assets/arrow" transform={rotateIcon ? 'rotate(90deg)' : 'rotate(0)'} >
And this is work but.... I need to props rotateIcon state to GrandParentComponent compoentent.
It’s not just the child to the parent. Than child to parent and from parent another level up to. GrandParent.
How do I do that? Is this a good way at all? What are my alternatives? I do not use redux in the system, but api context!
To be clear. All three components are connected.
Each to each is a child parent!
As I see it you have 3 options:
Store the state props in the parent of all components, which is too complex for this purpose I think.
Use Redux to handle state management for the app
Use Context API to handle the state management for the app as a global state.
You can check out this blog for this:
https://www.loginradius.com/blog/engineering/react-context-api/#:~:text=What%20is%20Context%20API%3F,to%20parent%2C%20and%20so%20on.
I do however use and recommend using redux for complex operations like this. When you understand the idea behind redux it becomes very easy to use it.
store the state in the GrandParentComponent and pass through in between components as a prop to reach ChildComponent.
Props are meant to be passed from a parent component to a child component(s).
You can do prop drilling i.e, adding a state to <GrandParentComponent/> component and passing it as prop to the child components.
grandparent
export default GrandParentComponent(){
const [rotateIcon, setRotateIcon] = useState(false);
// pass the state and setState down to the children
return <ParentComponent rotateIcon={rotateIcon} setRotateIcon={setRotateIcon}/>
}
parent
export default ParentComponent({rotateIcon, setRotateIcon}){
// pass props to child component again
return <ChildComponent rotateIcon={rotateIcon} setRotateIcon={setRotateIcon}/>
}
child
export default ChildComponent({rotateIcon, setRotateIcon}){
const expandAttachment = useCallback(() => {
setRotateIcon(!rotateIcon);
}, [rotateIcon]);
<img src="assets/arrow" transform={rotateIcon ? 'rotate(90deg)' : 'rotate(0)'} >
}
There’s nothing inherently bad about prop drilling, but it gets harder to manage the state of the app as the complexity increases.
To avoid this we either use React Context or Redux. React Context is baked into react itself, so it's very lightweight and powerful at the same time. It is the perfect fit for a situation like yours.
To keep the answer short, read on how to implement React Context and useContext hook here.

Find out who is rendering a component when debugging React

Let's say I have MyComponent rendered in several different places in my application. Someone passes bad props and which cause an error in the component's code. I pause on the error, but what I don't know is: what component rendered me? What component rendered that component? Is there any way to see the "stack trace" of component instances which led to this render?
Yes, have a look here: https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html#component-stack-traces
Basically you can use the componentDidCatch.
When using React 15 or below
class MyComponent extends React.Component {
fallbackProps = {color: "red"};
render() {
const propsAreCorrect = checkProps(this.props);
if(!propsAreCorrect) alert("aah, something bad happend!");
const props = propsAreCorrect ? this.props : this.fallbackProps;
return <div color={props.color}></div>;
}
}
You can use the React chrome devtools plugin to see the rendered component tree. It is currently here:
https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
When selecting a component, you can even see the provided props.
If you need debugger information on breaks for example, you can use reacts error checking mechanisms (as per Nikitas answer)

React Context API Seems to re-render every component

I'm trying to use the new Context API in my app and it looks like every time I update the context, it re-renders any component connected to it regardless. I have a sandbox demo setup to see code and working issue. When you type in the input - the buttons context is rendered and vice-versa. My original thinking was that if you type in the input, only the input context would be printed out.
DEMO
Is this how it works or am I missing something?
Thanks,
Spencer
The way I avoid re-rendering with react context API:
First I write my component as pure functional component:
const MyComponent = React.memo(({
somePropFromContext,
otherPropFromContext,
someRegularPropNotFromContext
}) => {
... // regular component logic
return(
... // regular component return
)
});
Then I write a function to select props from context:
function select(){
const { someSelector, otherSelector } = useContext(MyContext);
return {
somePropFromContext: someSelector,
otherPropFromContext: otherSelector,
}
}
I have my connect HOC wrote:
function connect(WrappedComponent, select){
return function(props){
const selectors = select();
return <WrappedComponent {...selectors} {...props}/>
}
}
All together
import connect from 'path/to/connect'
const MyComponent ... //previous code
function select() ... //previous code
export default connect(MyComponent, select)
Usage
<MyComponent someRegularPropNotFromContext={something} />
Demo
Demo on codesandbox
Conclusion
MyComponent will re-render only if the specifics props from context updates with a new value, if the value is the same, it will not re-render. Also it avoid re-rendering on any other value from context that is not used inside MyComponent. The code inside select will execute every time the context updates, but as it does nothing, its ok, since no re-rendering of MyComponent is wasted.
That is the expected behaviour. Components as consumers re-renders when their provider data updates. Further more, shouldComponentUpdate hooks do not work on Consumers.
Quoting React's content API:
All Consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update.
For more info check here

How to make props immutable to prevent rerender in React?

I have been creating a small app using react.js. I take 'performance' into account excessively.
So I have a simple child component named "Spinner". My goal is to make sure this component never re-renders.
Here is my component:
import React, {PureComponent} from 'react';
export default class Spinner extends PureComponent {
render() {
return (
<div className="spinner">
<div className="bounce1"></div>
<div className="bounce2"></div>
<div className="bounce3"></div>
</div>
)
}
}
In the time of the re-rendering with 'react-addons-perf', the component is always rendering, I am using PureComponent because I want that component to render only one time, I read that I can use immutable props but I don't know how to make this possible.
If I make some like to this:
componentDidMount() {
this.renderState = false;
}
shouldComponentUpdate(nextProps, nextState) {
return (this.renderState === undefined) ? true : this.renderState;
}
It is rendering only one time, but I believe that there is a better way.
How do I avoid the re-render? or maybe How I can make a immutable props?
You don't need an extra logic for componentShouldUpdate, since you don't want your component to rerender ever.
Adding only this should be enough to prevent component to rerender:
shouldComponentUpdate(nextProps, nextState) {
return false
}
For stateless components that don't need props, we can use a combination of a Functional (stateless) component, and babel's React constant elements transformer to optimize the component creation, and prevent rerenders entirely.
Add React constant elements transformer to your build system. According to the docs the transformer will:
Hoists element creation to the top level for subtrees that are fully
static, which reduces call to React.createElement and the resulting
allocations. More importantly, it tells React that the subtree hasn’t
changed so React can completely skip it when reconciling.
Change the spinner to a stateless component.
const Spinner = () => (
<div className="spinner">
<div className="bounce1"></div>
<div className="bounce2"></div>
<div className="bounce3"></div>
</div>
);
export default Spinner;
You can make your props immutable in React by using Immutable JS to transform your array props into Lists and object props int Maps. With this library you can use simple checks to check the equality of arrays/objects (instead of going through keys/values for equality):
shouldComponentUpdate(nextProps) => {
this.props.complexObjectAsMap === nextProps.complexObjectAsMap
}
But since your component doesn't have any props - this doesn't look the right question. In your case I'd go with Ori Drori answer.

Stateless function components cannot be given refs

I try to access some refs in my component. But I have this error in the console.
withRouter.js:44 Warning: Stateless function components cannot be given refs (See ref "pseudo" in FormInputText created by RegisterForm). Attempts to access this ref will fail.
Here is my component:
class RegisterForm extends React.Component {
render() {
return (
<form action="">
<FormInputText ref="pseudo" type="text" defaultValue="pseudo"/>
<input type="button" onClick={()=>console.log(this.refs);} value="REGISTER"/>
</form>
);
}
}
Plus when I click on the button I got Object {pseudo: null}in the console. I would expect an object instead null.
I am not sure to understand why this is not working. Note that my react tree uses mobx-react.
Refs do not work with stateless components. It is explained in the docs
Because stateless functions don't have a backing instance, you can't attach a ref to a stateless function component.
Stateless components at the moment of writing actually have instances (they are wrapped into classes internally) but you can not access them because React team is going to make optimizations in the future. See https://github.com/facebook/react/issues/4936#issuecomment-179909980
You could also try using recompose it has a function called toClass.
Takes a function component and wraps it in a class. This can be used as a fallback for libraries that need to add a ref to a component, like Relay.
If the base component is already a class, it returns the given component.

Categories