having a major brain fart today. I've tried a bunch of solutions to variations of this problem to no avail. It's an off-week for me and my brain, so I'm sure I'm just missing something stupid.
I'm trying to figure out why my Input component is re-rendering in a way that the input loses focus every time it receives value changes.
I need the username value to be stored in the App component.
I'm guessing it has to do with this Route "component" attribute returning a function, but I'm not entirely sure. I've also tried adding a key prop to various places throughout the app, but no luck.
Here's a link to a watered down codesandbox project.
And this is where I'm guessing something's happening.
component={() => (
<Input value={username} handleValueChange={handleUserNameChange} />
)}
Try instead of component using the render function. In the documentation for component it mentions the component will be re-mounted/re-created every render, which is why you are losing focus:
When you use component (instead of render or children, below) the
router uses React.createElement to create a new React element from the
given component. That means if you provide an inline function to the
component prop, you would create a new component every render. This
results in the existing component unmounting and the new component
mounting instead of just updating the existing component. When using
an inline function for inline rendering, use the render or the
children prop (below).
<Route
exact
path="/input"
render={() => (
<Input
key={"foo"}
value={username}
handleValueChange={handleUserNameChange}
/>
)}
/>
Here is an example in action.
Related
I am creating a component that internally has other 2 components with Formik each one, I did it in that way because each internal component submits separately on blur.
What I want to do is, once Formik validates, the errors should be showed within the parent component.
This is a quick example of the structure:
<ParentComponent>
<FirstInputField onChange={firstChangeHandler} />
<Divider />
<SecondInputField onChange={secondChangeHandler} />
<ErrorMessages>I WANT ERRORS TO BE HERE</ErrorMessages>
</ParentComponent>
The first approach was to send an "onError(error)" to the child component and call it to update the state on the parent component with the errors but I am getting a warning.
Cannot update a component (parent) while rendering a different component (child)
So I'd like to see if there is another way to do this.
Context Api to manage the error state would solve your issue.
Reference for similar implementation you can check this post.
How to solve the problem attached in the drawing?
I am talking about the best possible way of getting used without using redux.
When a button is pressed in a nested component, something has to change in another (it does not inherit from itself)
For example, in one component I choose the element and in the other I want to display details.
You can have a shared parent hold the state you want to change and pass to the first component while sending the onClick function to the other. Then, when one component changes the state through the onClick function, the changed prop will be passed on to the second component.
You should not change the state from one component via another component:
https://reactjs.org/docs/faq-state.html
props get passed to the component (similar to function parameters)
whereas state is managed within the component (similar to variables
declared within a function).
For "States" between components, you should use props from the store, via react-redux
First of all, I will suggest you to look into lift state up in react.
Now, how you'll do it: (just a pseudo example)
ParentComponent
onClick={this.onClick} stateProps={this.state.stateProps}
onClick() {
this.setState()
}
ComponentA
onClick={props.onClick}
ComponentB
console.log(props.stateProps)
The component will be used like: (again just a pseudo example)
<ParentComponent onClick={this.onClick} stateProps={this.state.stateProps}>
<ComponentA onClick={props.onClick} />
<ComponentB stateProps={props.stateProps} />
</ParentComponent>
I have this route (simplified)
<Route
path="/foo/:id">
render={({match}) => (
<Page key={match.params.id} id={match.params.id}/>
)}
</Route>
Now the reason for that is that when I go from /foo/1 to /foo/2 I don't want to clear and update state of an existing component, it's too complex in my case. So I have a dynamic key which causes to unmount the /foo/1 component and mount a brand new /foo/2 component.
So far so good. But here is the catch. The lifecycle I observed is this
/foo/1 re-render
/foo/2 constructor
/foo/2 componentDidMount
/foo/1 componentWillUnmount
/foo/2 render
Why doesn't the first component unmount before the second one mounts? Because of that I get weird re-renders and glitches (it's connected to redux store and initialization of the /foo/2 component changes the store which manages to re-render still existing component /foo/1). Any ideas how to avoid this?
Thanks
It is hard to determine exactly what is happening with your short snippet of code, but, I believe your issues are relating to some anti-patterns;
A react component constructor should only be used for two things:
Initialising local state
Binding event handlers
https://reactjs.org/docs/react-component.html#constructor
Secondly you are using the render prop with a inline function, this means when ever a matching route is detected and the parent component re-renders it will re-construct the component, un-mounting it first then mounting it again. To prevent this you should be defining the render function outside of the parents render function.
Thirdly because you have a dynamic route /foo/:id and assigning the value of :id to the key of the child component react sees it as a different component and doesn't unmount it until the current render process has reached the un-mounting stage. This means that for a short period of time you have <Page key={1} /> & <Page key={2}/> present.
Because these three issues, you are getting what seems to be a weird component lifecycle but in fact it is react doing exactly what you told it to do.
The Solution:
Move the redux action being invoked in the constructor of the <Page /> to be in the componentDidMount() function.
Move the inline render function to be outside the parent render function.
Remove the key prop.
I'm making an app that displays a lot of graphs, so in order to prevent unnecessary re-rendering I've been going through my components and seeing where I can implement shouldComponentUpdate to improve efficiency. But after doing so in some of the higher up components I'm getting a weird bug with the graph component itself. I have the generic <Graph/> parent component with this render:
render() {
return(
<div>
{ this.getGraph() }
</div>
);
}
and the getGraph() essentially returns this:
return React.createElement(graphComponentMapping[graphType], {data:this.state.data,...this.state.configuration});
where graphComponentMapping[graphType] is a <Table/> component (or various other ones, but we'll focus on table for now). Now in that <Table/> component I render this:
return(
<ReactTable {...this.getParameters()} data={data} columns={columns} />
);
and the getParameters() function essentially just looks through the configuration prop and gets all the properties relevant to the React Table component.
Now, when I initially render the <Table/>, it has the correct configuration (i.e. if I change the initial config the table matches that). But when I update the configuration prop in the <Graph/> parent, the table doesn't update accordingly. The render function in <Table/> is called again, and if I print this.props in the table's render I can see that various configuration properties have changed, e.g. this.props.defaultPageSize has changed from say 5 to 3, but the table doesn't re-render to reflect this. Even in the browser's React dev tools I can see the props have changed.
If I force a complete a re-render of the element by using a random key, e.g.
return(
<ReactTable {...this.getParameters()} data={data} columns={columns} key={Math.random()} />
);
then it works, and the table updates when I pass new configuration props. Why doesn't it update when receiving new props, only when I force it to completely re-render?
I have faced such problem but that was primarily the way my redux store was setup.
Redux object is reference based so even just updating the properties, it wasn't telling my React component that something has changed. To solve this, I had to create a new object and update value there and then use it, which solved the issue.
this could be the reason they key having a random number is causing your whole component to re-render.
Our current app has each screen implement a AppLayout component, that takes a body and a header prop.
While the body prop is often very different, the (quite complex) header has a lot of repeating content between the various screens. At times just the title text is changing.
Currently, going from one route to another rerenders the entire screen, a quite wasteful operation given the similar content. Is there a way of avoid re-rendering and re-mounting of common sub-components between two screens?
Example of routing setup
This shows how a lot is seemingly repeated in the trees.
<Route
exact
path='/user'
component={()=> {
return (<AppLayout
header={<AppHeader heading="Users"/>}
main={<UserList/>} />);
} />
<Route
exact
path='/user/new'
component={()=>{
return (<AppLayout
header={<AppHeader heading="Create user"/>}
main={<UserAccountAdd />} />);
} />
<Route
exact
path='/user/:userId'
component={({ match }) =>
return (<AppLayout
header={<AppHeader heading="User profile"/>}
main={<UserAccountInfo userId={match.params.userId}/>}
/>)
} />
The AppHeader component basically looks like
connected(div)
ReallyExpensiveFoo
ReallyExpensiveBar
<p>{this.props.title}</p>
Basically I would like to have seen that the <p> tag of header that renders the heading title should remount.
React doesn't update whole DOM on each state update. Instead it lets each class component to decide if it should be re-rendered by calling its shouldComponentUpdate() method. For functional components it, most likely, simply compares component props to decide if component should re-render.
You can avoid re-rendering of the component by converting your component into class component and by implementing your own version of shouldComponentUpdate(). However normally it is not necessary as default behavior (or versions provided by e.g. Redux's connect() HoC) are smart enough to avoid unnecessary re-rendering and provide good application's performance.
React Router has a good SideBar example on their site that is quite relevant. Basically it injects pairwise Routes at different parts in the hierarchy, so they update independently to routing changes.