I want to update all the components in my React application (no matter what is the nesting level or if there is any prop or state change).
I tried using this.forceUpdate() but as some of the components don't use any props or state, those and their children are not updating.
Also, I tried the solution mentioned in the example but this also didn't work.
App.js
render() {
return (
<div>
<Routes />
</div>
);
}
As you can see, Routes don't take any props, so this component is not re-rendering. As a result of which, none of the components inside Routes is updating.
One way is using the store. As this is a small operation only, so I don't want to use the store for this kind of use case.
If you really need to update components on every render, you can use a key prop that's different every time the component renders. This will force React to unmount the previous instance of component and mount the new one with all the state and props being reset. In case of Route the easiest is to assign key as the current page url:
render() {
return (
<div>
<Routes key={window.location.href} />
</div>
);
}
With other components it's a bit more tricky, since you'll need to manually ensure the key is different each time.
If you would like to have more granular control over when the components are updated, you can hook the key prop to the state and change it onClick.
This post explains the approach in more detail.
Related
I want to update the state of my application when a router component is rendered. For example, if I go home "/" I will render homepage, but I want to trigger a state change so that other components like my footer and header are aware we are on the home page vs internal page. Currently, I am parsing the url manually when site is loaded to detect this, however, as the site grows this is unfeasible. I have also seen onEnter as a solution, however, this is not available for reach router.
<Router basepath={process.env.PUBLIC_URL} className="app" >
<LandingPageBody path="/" default/>
//update redux state to show we are on homepage
</Router>
You can use componentDidMount in LandingPageBody component. It is a react lifecycle method it will be triggered when component will mount. You can update state from inside of this method.
If you are using functional components, make use of useEffect function with dependency on props.match.location. Check for condition in that and dispatch action if the condition is true.
(React.useEffect(() => { if(props.match.location === 'desired path') dispatch(action) }, [props.match.location]))
This way when ever your route component loads, it will check for route on every route change and let your redux store know about the current path.
For Stateful component, you can do the same in componentDidUpdate lifecycle.
React newbie here: I have noticed that the component state gets cleared on unmount. is there a way to prevent it? is it using redux?
As you say, when a component is unmount you can not access to the state. And thats the way it is because the lifecicle of the component.
What you can do is try to save the state of the component that was instantiated while it is mounted and every time it is updated.
You can use the storage and use simple javascript.
You can have the state in the parent or another ancester instance.
You can have the state in the redux store. Note that your component will receive the vars as props, so it wont be the state properly said.
You can use redux in order to manage the state and the states values through time. I recommend you the redux-devtools-extension and also this article about it.
You have a plethora of options. You can either use a state management tool, like redux, context API and so on, or you can pass-in a callback to your parent component and trigger it on childComponentWillUnmount like this:
ParentComponent.jsx:
childComponentWillUnmount = (data) => {
console.log('my data', data);
}
render(){
return <div>
<Child componentUnmountCallback={this.childComponentWillUnmount()}/>
<div>
}
<div>
Child.jsx
...
componentWillUnmount() {
this.props.childComponentWillUnmount(this.state);
}
...
So, I'm using Apollo-link-state for local state management on an app I'm building, and I have a Modal component that pops up if modalData in the state changes. For it to listen to this, it's wrapped in a <Query> component, like so:
render(){
return (
<Query query={GET_MODAL_DATA}>
{({data}) => {
const currentModal = data.currentModal;
return (
<CSSTransition in={currentModal.operation ? true : false} unmountOnExit classNames="modal" timeout={{ enter: 1, exit: 200 }}>
<div className="modal-container" onClick={this.closeModal}>
<div onClick={e => e.stopPropagation()}>
<h2>
{ currentModal.operation+' '+currentModal.dataType }
</h2>
</div>
</div>
</CSSTransition>
)
}}
</Query>
)
}
with this arrangement, however, it re-renders (and re-queries) no matter what you do.
Change route? Re-renders. Change another component's local state? Re-renders. Change text fields? Re-renders.
Perhaps this is how Apollo is meant to work, but coming from Redux it seems kinda inefficient for every mounted component to re-query if you so much as breathe in the direction of your browser.
Is that what it's meant to do, or have I set something up incorrectly?
react-apollo will re-query every time unless you set the fetchPolicy to network-only.
As for the re-renders, it is more of a React issue than it is apollo.
You see, every time a state or a prop changes, react will run the component render() function. If react finds that the props for a component changed, it will re-render the component.
Since you are using an inline function as the children prop for Query component, this will cause re-rendering every time because every instance of a function is a reference to a different memory address, and thus is perceived as different. What you could do about this is to take out the function into: let's say, this.renderModal. Then, just pass the function a children to Query like this:
<Query>{this.renderModal}</Query>
This will ensures that the children prop passed to Query is the same function every time.
Second, you are passing a literal object { enter: 1, exit: 200 } to CSSTransition component. This has the same issue as above. Every instance of an object also has reference to different memory address, even if the value is exactly the same. One thing you can do about this is to take out the object, put it into constructor like
this.transitionTimeout = { enter: 1, exit: 200 };
and then pass that variable as a prop like:
<CSSTransition timeout={this.transitionTimeout}
This will ensures that the prop will point to the same object every time, thus preventing re-render.
Update:
You might also want to checkout about React.PureComponent. Here is an article that explains about it pretty well. Basically react will compare the value of the previous and next props shallowly, and if it is equal, it will not trigger render.
trying to understand React-Redux, i find it unusual that all my components get new props when ever any slice of the state get changed. so is this by design or i'm doing something wrong ?
example App
class App extends React.Component {
render(){return (
<div>
<Navbar data={this.props.navbar} />
<Content data={this.props.content} />
</div>);
}
}
select (state) => ({ navbar:state.navbar, content:state.content});
export default connect(select)(App);
Components
export const NavbarForm = props => {
console.log('RENDERING with props--->',props);
return (<h1>NAV {props.data.val}</h1>);
};
export const ContentForm = props => {
console.log('RENDERING CONTENT with props--->',props);
return (<h1>CONTENT {props.data.val}</h1>);
};
////////INDEX.js//////
const placeholderReducer = (state={val:0},action)=>{
//will update val to current time if action start with test/;
if(action.type.indexOf('TEST/') === 0)return {val:Date.now();}
return state;
}
export const rootReducer = combineReducers({
navbar:placeholderReducer,
content: (state,action)=>(state || {}), //**this will never do a thing.. so content should never updates right !!**
});
const store = createStore(rootReducer, {}, applyMiddleware(thunk));
render( <Provider store={store}> <App /></Provider>, document.getElementById('app')
);
setInterval(()=>{ store.dispatch(()=>{type:'TEST/BOOM'}) },3000);
okay in this app, what i expect is that Navbar component will get updated every 3000ms while content component will never updates because its reducer will always return the same state.
yet i find it really strange that both components does reRender every time an action is fired.
is this by design ? should i worry about performance if my app has 100+ component ?
This is entirely by design. React assumes that your entire app will be re-rendered from the top down by default, or at least a given subtree will be re-rendered if a certain component does a setState or something similar.
Because you only have the very top component in your app connected, everything from there on down is React's standard behavior. A parent component re-renders, causing all of its children to re-render, causing all of their children to re-render, and so on down.
The core approach to improving UI performance in React is to use the shouldComponentUpdate lifecycle method to check incoming props and return false if the component does not need to re-render. This will cause React to skip re-rendering that component and all of its descendants. Comparisons in shouldComponentUpdate are generally done using shallow reference equality, which is where the "same object references means don't update" thing becomes useful.
When using Redux and connect, you will almost always find yourself using connect on many different components in your UI. This provides a number of benefits. Components can individually extract the pieces of the store state that they need, rather than having to hand them all down from the root component. In addition, connect implements a default shouldComponentUpdate for you, and does a similar check on the values you return from your mapStateToProps function. So, in a sense, using connect on multiple components tends to give you a "free win" in regards to performance.
Further reading on the topic:
Redux FAQ: Connecting multiple components
React/Redux Links: Performance articles
Yes this is by design. Action is dispatched. Reducers run. Store subscribers get notified "the store has changed". Connected components are store subscribers.
Typically you just don't worry about it until you can actually measure a performance problem that you can attribute to this - don't prematurely optimize.
If you find out that it is a problem, then you can do one of the following:
Add a shouldComponentUpdate method to your components so they can see that the props they received aren't different and do not need to render (there are lots of Pure Render mixins & high order components available to make this easy)
Instead of connecting the top-level app, connect the Navbar and Content components directly. The App will never rerender, but the children will if the store changes. And react-redux automatically uses shouldComponentUpdate to only re-render the connected components that actually have new props.
Let's say I created a component which can be turned on/off based on state.
var onOff = React.createElement(<OnOff />, mountElement);
onOff.setState({ on: false });
Later I'm creating a new component called Parent, which will use OnOff inside it.
render() { return <div><OnOff /></div> }
Now how can I change the OnOff state? There is no way I can call setState on it. And I should not according to React doc. So I have to add initial state to OnOff's props:
constructor(props) {
super(props);
this.state = { on: props.initialOn };
}
then in Parent's render method, set the initialOn prop with its state:
render() { return <div><OnOff initialOn={false} /></div> }
But it's still not working, because whenever I change Parent's state, the OnOff component inside it is not re-created with new initial state. Instead, it is only re-rendered with old state. I have a CodePen to prove it: http://codepen.io/anon/pen/QjMwjO?editors=101
You can update the state of the OnOff component by declaring the update also inside a componentWillReceiveProps function, something like:
componentWillReceiveProps:
function(nextProps) {
this.setState({
on : nextProps.initialOn
});
}
This allows you to update state, when new props arrive. And it is valid react.
You should however consider if you need state in OnOff at all: if the only initial setting and all updates ONLY come from its parent component, then a stateless component would be better.
One of the important things to understand when "Thinking in React" is to figure out which component does State belong to.
Read this in React docs
What Components Should Have State?
Most of your components should simply take some data from props and render it. However, sometimes you
need to respond to user input, a server request or the passage of
time. For this you use state.
Try to keep as many of your components as possible stateless. By doing
this you'll isolate the state to its most logical place and minimize
redundancy, making it easier to reason about your application.
A common pattern is to create several stateless components that just
render data, and have a stateful component above them in the hierarchy
that passes its state to its children via props. The stateful
component encapsulates all of the interaction logic, while the
stateless components take care of rendering data in a declarative way.
Thus, your OnOff should not have state but use properties passed down from the parent instead. I have illustrated this at http://codepen.io/anon/pen/gaxbGm?editors=101
render() {
writeLog("OnOff render called!")
writeLog("Child: " + this.props.initialOn)
return <span>{this.props.initialOn ? "On" : "Off"}</span>;
}
I would also recommend reading "Thinking in React" to get further clarity.