React complains about this code? Why?
componentDidMount() {
<childComponent />;
}
I need to show childComponent only once when the page loads.
As several of the comments indicate, there is no point to conditionally rendering a component on componentDidMount. That is what the render function is for. The reason for this is that generally the entire React application will be loaded onto the client as soon as the applications URL is hit.
There are instances where one would need to wait for resources or data to load before rendering however:
Lazy loading: It is possible to separate your application into chunks. Those chunks could then be delivered to the client only when needed. Read more about this in the React docs.
Below is an example of one possible implementation (also from the docs):
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
Waiting on data: It is also possible that you may only want to render your component (or some portion of your component) once data is returned from your server. In this case you may decide to initiate your server request in componentDidMount, then conditionally render something once the data is returned.
Below is an example of one possible implementation using hooks (out with 16.8):
const DataHandlerComponent = () => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [submitting, setSubmitting] = useState(true);
// equivalent to componentDidMount
useEffect(() => {
axios.get('some url')
.then(({ data }) => {
setData(data);
setSubmitting(false);
})
.catch((err) => {
setError(err);
setSubmitting(false)
});
}, []);
// equivalent to render
return (
<div>
{submitting &&
<YourCustomProgress />
}
{!submitting && data &&
<YourCustomComponent data={data} />
}
{!submitting && !data &&
<YourCustomErrorComponent error={error} />
}
</div>
);
};
Hopefully one of these options will fit your needs. If you don't need one of these two approaches, you can probably just render your component in the normal render cycle.
<MyComponent> is just syntactic sugar for abstracting element creating and event binding. It does not make any sense to use it elsewhere than in render.
If you want to show the component only when the page load, you shall update the state and use state-conditioned rendering. React takes it from there, optimizing many thing you didn't think about, and allowing you to have a pattern-compliant, easy to maintain, and easy to debug React application. Something in the lines of:
export default class MyComponent {
constructor(props) {
super(props);
this.state = {
display = false;
}
}
componentDidMount() {
this.setState({display: true});
}
render() {
return this.state.display ? <ChildComponent/> : <div>Not yet</div>;
}
}
class demo extends Component{
constructor(props){
super(props);
this.state = {loading : true}
}
componentDidMount() {
this.setState({loading : false});
}
render(){
return (
{(()=>{
if(this.state.loading == false){
return(
<childComponent />
)
}else{
return (
<div>loading...</div>
)
}
})}
)
}
}
Related
I am learning reactjs and I wrote component with the method componentWillReceiveProps (cWRP) but I read that it is deprecated and it must replace with getDerivedStateFromProps (gDSFP) - https://en.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html.
Please note that the following code has the sole purpose of illustrating my problem and questions. It is not a full code.
App.js file :
import React from 'react';
import './App.css';
import Display from './component.js'
class App extends React.Component {
state={resetCounter:false}
resetCounter= () => this.setState( {resetCounter: true} );
render() {
return (
<div className="App">
<header className="App-header">
<Display resetCounter={this.state.resetCounter}></Display>
<div>
<p></p><p></p>
<button onClick={this.resetCounter}>Reset</button>
</div>
</header>
</div>
);
}
componentDidUpdate () {
if (this.state.resetCounter!==false)
this.setState( {resetCounter: false} );
}
}
export default App;
component.js file
import React from 'react'
class Display extends React.Component {
constructor() {
super();
this.state = this.resetState();
this.state.generalCounter=0;
}
/* method to avoid code duplication in constructor and cWRP
could not be used with getDerivedStateFromProps */
resetState = () => ({resettableCounter: 0,});
componentWillReceiveProps(nextProps) {
if (nextProps.resetCounter===true)
this.setState(this.resetState())
}
render() {
return (
<>
<div>
<div>general counter : {this.state.generalCounter}</div>
<div>resettable counter : {this.state.resettableCounter}</div>
</div>
<div>
<button onClick={this.incCounters}>+</button>
<button onClick={this.decCounters}>-</button>
</div>
</>
)
}
incCounters= () => this.setState(
{
resettableCounter: this.state.resettableCounter+1,
generalCounter: this.state.generalCounter+1
}
)
decCounters= () => this.setState(
{
resettableCounter: this.state.resettableCounter-1,
generalCounter: this.state.generalCounter-1
}
)
}
export default Display
In the state of the component, there is a resettable part and a non resettable one. A method resetState is used to avoid code duplication in the constructor and in cWRP.
To replace cWRP by gDSFP, I wrote a class method because instance method could NOT be called in gDSFP (this is not usable)
...
constructor() {
super();
this.state = Display.resetState();
this.state.generalCounter=0;
}
static resetState () {
return ({resettableCounter: 0,});
}
static getDerivedStateFromProps(nextProps) {
if (nextProps.resetCounter === true) {
return Display.resetState();
} else {
return null;
}
}
...
With this solution, it is very easy to modify all my components but I am not sure that it is a good mean.
I wonder if I have a misconception and if I should rewrite my components to separate them into Fully controlled components and Fully uncontrolled components with a key ( https://en.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#preferred-solutions).
For example, in this case, do I have to write :
One Fully uncontrolled components for the resettable counter
One Fully controlled one for the non resettable counter
A parent component with the +/- buttons to render them.
I ask this question because in some cases, it will be much work, so I want to be sure before continuing.
You would want to keep the gdsfp version in your post if your component depends on some outside props, which you don't have controll over (such as JSON returned or 3rd party render props component, etc).
It looks like you have a full control over what's passed down to the Display. You can pass down an initial resettableCounter value down to Display.
The advantage is two-folds.
Your Display props shows what the Display does - Making it more descriptivie/readable.
It's easier to maintain, as you don't have to massage the data.
For your particular case, Fully uncontrolled component with a key seems to make more sense, as Display should accept the initial value to show, but is responsible for managing the reseetableCounter.
Unless it's absolutely unavoidable, don't create components which control their siblings (or parents). Instead, lift state to a common ancestor:
const Display = ({
generalCounter,
resettableCounter,
incrementCounters,
decrementCounters,
}) => (
<div>
<div>General Counter: {generalCounter}</div>
<div>Resettable Counter: {resettableCounter}</div>
<button onClick={incrementCounters}>Increment</button>
<button onClick={decrementCounters}>Decrement</button>
</div>
);
class DisplayContainer extends React.Component {
state = {
generalCounter: 0,
resettableCounter: 0,
};
incrementCounters = () => this.setState(prevState => ({
generalCounter: prevState.generalCounter + 1,
resettableCounter: prevState.resettableCounter + 1,
}));
decrementCounters = () => this.setState(prevState => ({
generalCounter: prevState.generalCounter - 1,
resettableCounter: prevState.resettableCounter - 1,
}));
resetResettableCounter = () => this.setState({
resettableCounter: 0,
});
render() {
return (
<React.Fragment>
<Display
{...this.state}
incrementCounters={this.incrementCounters}
decrementCounters={this.decrementCounters}
/>
<button onClick={this.resetResettableCounter}>
Reset Resettable Counter
</button>
</React.Fragment>
);
}
}
const App = () => (
<div>
<DisplayContainer />
</div>
);
An alternative approach would be something like Redux (which effectively lifts state out of React).
I have a small react app. In App.js I have layout Sidenav and Content area. The side nav is shown on some page and hid from others. When I go to some components with sidenav, sidenav flag is set by redux and render the component again, in the componentDidMount I have api call, and it is executed twice.
class App extends Component {
renderSidebar = () => {
const {showNav} = this.props;
return showNav ? (
<TwoColumns>
<Sidenav/>
</TwoColumns>) : null;
};
render() {
const {showNav} = this.props;
const Column = showNav ? TenColumns : FullColumn;
return (
<Row spacing={0}>
{this.renderSidebar()}
<Column>
<Route exact path="/measurements/:id/:token/:locale/measure"
component={MeasurementPage}/>
</Column>
</Row>
)
}
}
const mapStateToProps = state => ({
showNav: state.sidenav.showNav
});
export default connect(mapStateToProps)(App);
I tried to use shouldComponentUpdate to prevent the second API call
class MeasurementPage extends Component {
constructor(props) {
// This update the redux "showNav" flag and re-render the component
props.toggleSidenav(false);
}
shouldComponentUpdate(nextProps) {
return !nextProps.showNav === this.props.showNav;
}
componentDidMount() {
// This is executed twice and made 2 api calls
this.props.getMeasurement(params);
}
render() {
return <h1>Some content here</h1>;
}
}
const mapStateToProps = state => ({
showNav: state.sidenav.showNav
});
export default connect(mapStateToProps)(MeasurementPage);
Did someone struggle from this state update and how manage to solve it?
This props.toggleSidenav(false) might cause side effect to your component lifecycle. We use to do this kind of stuff inside componentWillMount and it has been depreciated/removed for a reason :). I will suggest you move it inside componentDidMount
class MeasurementPage extends Component {
constructor(props) {
// This update the redux "showNav" flag and re-render the component
// props.toggleSidenav(false); // remove this
}
shouldComponentUpdate(nextProps) {
return nextProps.showNav !== this.props.showNav;
}
componentDidMount() {
if(this.props.showNav){ //the if check might not necessary
this.props.toggleSidenav(false);
this.props.getMeasurement(params);
}
}
render() {
return <h1>Some content here</h1>;
}
}
The comparison should be
shouldComponentUpdate(nextProps) {
return !(nextProps.showNav === this.props.showNav)
}
The problem is that !nextProps.showNav negate showNav value instead of negating the role expression value, and that is why you need an isolation operator.
It's No call twice anymore.
componentDidMount() {
if (this.first) return; this.first = true;
this.props.getMeasurement(params);
}
I have a HealthForm component where the user enters an url in a text input and clicks a button to submit. I have that url saved as a state in the component and I call some APIs which are all working.
The problem is that I have several other components that need that url and I can't seem to find a way to pass it to them.
My App.js looks like this which is why all other posts/tutorials are confusing me.
class App extends React.Component {
render(){
return(
<MuiThemeProvider>
<Router>
<div className="App">
<Route path="/" component={()=>(
<div>
<Header/>
<HealthForm/>
</div>)}/>
<Route path="/path1" component={ProductForm}></Route>
<Route path="/path2" component={xForm}></Route>
<Route path="/path3" component={yForm}></Route>
<Route path="/path4" component={zForm}></Route>
</div>
</Router>
</MuiThemeProvider>
);
}
}
HealthForm
class HealthForm extends React.Component {
constructor(props) {
super(props);
this.state = {
jarvisURL: '',
jarvisURLError: '',
status: '',
showStatus: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
validate = () => {
//…checks for input errors
}
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
handleSubmit(event) {
event.preventDefault();
const err = this.validate();
let that = this;
if (!err) {
this.setState({
jarvisURLError: ''
});
console.log(this.state);
var data = this.state.jarvisURL
//… a fetch API call
}
}
render() {
return (
<form>
<TextField
...
/>
<br/>
<Button variant="contained" size="small" color="primary" onClick={e => this.handleSubmit(e)} >
Check
</Button>
<br /> <br />
...
</form>
);
}
}
export default HealthForm;
EDIT
Decided to do the suggestion from Grim
However the saga continues here with another problem: react page refreshes
It is correct that horizontally flowing data is frowned upon, but the reality is that in complex applications its often unavoidable. This is exactly why React provides the Context API. You start by creating a context for the Jarvis data:
import React from "react";
export const JarvisContext = React.createContext();
export class JarvisProvider extends React.Component {
constructor(props) {
super(props);
this.state = {
url: null,
setUrl: this.setUrl
};
}
render() {
return (
<JarvisContext.Provider value={this.state}>
{this.props.children}
</JarvisContext.Provider>
);
}
setUrl = (url) => {
this.setState({url});
}
}
Then anytime you need to access the Jarvis state (either setting or getting it) you use the Context.Consumer which passes the current state (which can include setter methods).
First you need to wrap any code that might use the JarvisContext.Consumer in the JarvisProvider. Note that you can also pass props to the provider to set the initial state. I often do this globally in the App.js component, but you can put it anywhere you need it.
<JarvisProvider>
<HealthForm />
</JarvisProvider>
Then consume it in the HealthForm render method:
render() {
return (
<JarvisContext.Consumer>
{({url, setUrl}) => {
<form>
<input type="text" value={url} onChange={(e) => setUrl(e.target.value)} />
</form>
}}
</JarvisContext.Consumer>
);
}
Note that this is not an exact copy of your functionality, but should be a good starting point. Saludos!
So there are a couple ways of doing this. The recommended method would be either the Context API or Redux. However there are some times where this can't be used in an Enterprise environment (I've worked places where Redux is used as an API cache and not used to store data set within the application). Another solution, albeit not best practices, is event bubbling.
With event bubbling you can pass a function as a property to the child component which then bubbles up to the parent component. This is done for several components such as the MaterialUI Button where you pass in a onClick listener. Using your handleClick function you would have something like:
handleSubmit(event) {
event.preventDefault();
const err = this.validate();
let that = this;
if (!err) {
this.setState({
jarvisURLError: ''
});
console.log(this.state);
var data = this.state.jarvisURL
//… a fetch API call
this.props.jarvisURLUpdated(this.state.jarvisURL);
}
}
Where jarvisURLUpdated is a function that calls the parent function and you can set the state of the parent as needed. The parent can then pass in the URL to the desired children.
Another option is the usage of local storage or cookies to store the URL in the browser and reuse (also not recommended by convention). This tends to work a bit better than event bubbling if you're using something like react-router-dom and displaying the information on separate pages (Redux would eliminate the event bubbling issue due to a global state).
Either use ContextAPI or any state management API like redux.
I'm doing a simple project that has something like 3 forms and right now I start the component with empty Inputs and then request data from API to pre-populate the form using the componentWillMount() hook.
It works for me now but if someday my app need more and more data it would be annoying to do this everytime for any new form and I would like to know if there is any lib or pattern to help pre-populating forms without using any state container (Redux, mobx, and I really don't know if they are needed in this case).
It is better to do your data fetching in componentDidMount than in componentWillMount:
If you need to load data from a remote endpoint, this is a good place
to instantiate the network request.
If you want to reuse some data fetching logic without any external state you could use Component with render props or Higher Order Components.
For example:
function withData(fetchData) {
return BaseComponent => {
class WithData extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentDidMount(){
fetchData().then(response => {
this.setState({ data: response })
})
}
render(){
return <BaseComponent {...this.props} data={this.state.data} />
}
}
return WithData;
}
}
And later you can reuse this logic:
const DataList = ({ data }) => (
<ul>
{
data.map(item =>
<li>{item.name}</li>
)
}
</ul>
)
// passing Promises as a `data` producers
const UserDataList = withData(fetchUsers)(DataList);
const GroupDataList = withData(fetchGroups)(DataList);
const CatsDataList = withData(() => fetchAnimals('cats'))(DataList);
const ListOfEverything = () => (
<Container>
<UserDataList />
<GroupDataList />
<CatsDataList />
</Container>
)
I was reading the section on Don’t Mutate the Original Component. Use Composition from this link.
https://reactjs.org/docs/higher-order-components.html
I then reviewed a project I'm trying to build. At a high level, this is what my code looks like:
class Wrapper extends Component {
constructor(props) {
this.wrappedComponent = props.wrappedComponent;
}
async componentWillAppear(cb) {
await this.wrappedComponent.prototype.fetchAllData();
/* use Greensock library to do some really fancy animation on the wrapper <Animated.div> */
this.wrappedComponent.prototype.animateContent();
cb();
}
render() {
<Animated.div>
<this.wrappedComponent {...this.props} />
</Animated.div>
}
}
class Home extends Component {
async fetchAllData(){
const [r1,r2] = await Promise.All([
fetch('http://project-api.com/endpoint1'),
fetch('http://project-api.com/endpoint2')
]);
this.setState({r1,r2});
}
animateContent(){
/* Use the GreenSock library to do fancy animation in the contents of <div id="result"> */
}
render() {
if(!this.state)
return <div>Loading...</div>;
return (
<div id="result">
{this.state.r1.contentHTML}
</div>
);
}
}
export default class App extends Component {
render() {
return <Wrapper wrappedComponent={Home} />;
}
}
My questions are:
In my Wrapper.componentWillAppear(), I fire the object methods like this.wrappedComponent.prototype.<methodname>. These object methods can set it's own state or animate the contents of the html in the render function. Is this considered mutating the original component?
If the answer to question 1 is yes, then perhaps I need a better design pattern/approach to do what I'm trying to describe in my code. Which is basically a majority of my components need to fetch their own data (Home.fetchAllData(){then set the state()}), update the view (Home.render()), run some generic animation functions (Wrapper.componentWillAppear(){this.animateFunctionOfSomeKind()}), then run animations specific to itself (Home.animateContent()). So maybe inheritance with abstract methods is better for what I want to do?
I would probably actually write an actual Higher Order Component. Rather than just a component which takes a prop which is a component (which is what you have done in your example). Predominately because I think the way you have implemented it is a bit of a code smell / antipattern.
Something like this, perhaps.
class MyComponent extends React.Component {
constructor() {
super();
this.animateContent = this.animateContent.bind(this);
}
componentWillReceiveProps(nextProps) {
if (this.props.r1 !== nextProps.r1) {
this.animateContent();
}
}
componentDidMount() {
// do your fetching and state setting here
}
animateContent() {
// do something
}
render() {
if(!this.props.r1) {
return <div>Loading...</div>;
}
return (
<div id="result">
{this.props.r1.title}
</div>
);
}
}
const myHOC = asyncFn => WrappedComponent => {
return class EnhancedComponent extends React.Component {
async componentDidMount(){
const [r1, r2] = await asyncFn();
this.setState({ r1, r2 })
this.animateContent();
}
animateContent = () => {
// do some animating for the wrapper.
}
render() {
return (<WrappedComponent {...this.props} {...this.state} />)
}
}
}
const anAsyncExample = async () => {
const result = await fetch("https://jsonplaceholder.typicode.com/posts");
return await result.json();
}
const MyEnhancedComponent = myHOC(anAsyncExample)(MyComponent);
Here's a working JSFiddle so you can see it in use:
https://jsfiddle.net/patrickgordon/69z2wepo/96520/
Essentially what I've done here is created a HOC (just a function) which takes an async function and returns another function which takes and a component to wrap. It will call the function and assign the first and second result to state and then pass that as props to the wrapped component. It follows principles from this article: https://medium.com/#franleplant/react-higher-order-components-in-depth-cf9032ee6c3e