React console.log showing duplicate results in Dev Tools - javascript

Why does React show duplicate console.log? I found that to remove StrictMode from index.js. but there was no such problem before and why did I remove StrictMode instead of fixing the issue. What other issues could there be?
see the screenshort: https://prnt.sc/HLAmthr9efoB
import React, { useEffect, useState } from "react";
import Country from "../Country/Country.js";
const gridStyle = {
display: "grid",
gridTemplateColumns: "repeat(4, 1fr)",
gridGap: "20px",
};
const Countries = () => {
const [countries, setCountries] = useState([]);
useEffect(() => {
fetch("https://restcountries.com/v3.1/all")
.then((res) => res.json())
.then((data) => setCountries(data));
}, []);
console.log(countries);
return (
<div className="all-countries">
<p>{countries.length}</p>
<div style={gridStyle} className="country-container">
{countries.map((country) => (
<Country key={Math.random() * 500000} country={country}></Country>
))}
</div>
</div>
);
};
export default Countries;

For React.StrictMode issue, the solution can be your rendered component which renders twice that maybe wrap with React.StrictMode. Comment out or clean up the React.StrictMode, I hope it will fix.
In StrictMode, components are rendered twice for ensuring (on development but not production) in order to detect any problems with your code and warn you about them (which can be quite useful).
If StrictMode is enabled in your code, it might be because you used create-react-app or similar to create your app initially, which automatically enables StrictMode by default.

Most probably you write console.log() outside of function that trigger the log,
so will be fired every time the component is re-rendered.

Related

Why My React Component Render Multiple Times In Console?

Im new in react.
I'm Created two file App.js and UseEffect.js
I'm Learn about lifecycle in react with function.
So When I See in console, that's render multiple time.
You can see my picture below.
My Console In Browser
This Is My Code
UseEffect.js
import React, {useState, useEffect} from "react";
function MyFunction(){
console.log('-> Function Init')
const [count, setCount] = useState(0)
const handleCount = () => {
setCount(prevState => {
return prevState+1
})
}
//LifeCycle
useEffect(() => {
console.log('my first effect')
})
console.log(`-> Start Render (${count})`)
return(
<div>
<h1>Function Component</h1>
<p>
<button onClick={handleCount}>Count</button>
{count}
</p>
</div>
)}
export default MyFunction
App.Js
import './App.css';
import UseEffect from './components/UseEffect'
function App() {
return (
<div className="App">
<UseEffect />
</div>
);
}
export default App;
How do it's work?, I Want it. it's just render one times.
Your useEffect call is missing a dependency array. When you want it to run only at the initial render, you need to pass it an empty array as its dependencies.
useEffect(() => {
console.log('my first effect')
}, [])
For further details, see this question.
Why it renders twice:
It's an intentional feature of the StrictMode. This only happens in development, and helps find accidental side effects put into the render phase. We only do this for components with Hooks because those are more likely to accidentally have side effects in the wrong place.
-gaearon
TLDR: It's a feature not a bug.

In MobX, why are side effects bad in render functions

Recently I was asked:
Why can't I write to mobx state in a render function
I know Mobx disallows it, and it isn't good, however I also know you can get around Mobx's check simply by waiting a bit, for example: Promise.resolve().then(() => observable.value = "test").
I've had bad experiences with debugging code in other frameworks when programmers caused side effects in functional functions (render-like functions). However, most of my bad experiences involve really complicated bugs I still don't fully understand, so I couldn't come up with a simple explanation of why this is bad.
In the end I gave a fairly terrible answer, and just told the other programmer:
It's bad, just trust me, don't try to do that, it'll cause problems later on
What is a better answer to the question 'why can't I write to mobx state in a render function'?
NOTES:
I am specifically interested in any concrete examples of code that would fail (unexpectedly) from this.
I created an example of code that mutates Mobx state within render functions. Each component sets the global curPageName.value state when rendered, and displays this variable, and a different component also displays this variable. It's rather terrifying, but it technically works: https://codesandbox.io/s/mobx-playground-u8per?file=/src/index.js
import React from "react";
import ReactDOM from "react-dom";
import { observable } from "mobx";
import { observer } from "mobx-react";
let curPageName = observable({
value: "test"
});
const Test = observer(() => {
Promise.resolve().then(() => (curPageName.value = "Test"));
return <div>{curPageName.value}</div>;
});
const Test2 = observer(() => {
Promise.resolve().then(() => (curPageName.value = "Test2"));
return <div>{curPageName.value}</div>;
});
#observer
class MainPage extends React.Component {
state = {
page: Test
};
render() {
let Page = this.state.page;
return (
<React.Fragment>
<div>Cur Page: {curPageName.value}</div>
<div>
<button onClick={() => this.setState({ page: Test })}>Test</button>
</div>
<div>
<button onClick={() => this.setState({ page: Test2 })}>Test2</button>
</div>
<Page />
</React.Fragment>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<MainPage />, rootElement);

Tests React component with jest and enzyme

i have components presented below. I am totally new in unit testing. Can anyone give any one give me advice how and what should I test in this component? I was trying to shallow render it, to check is text in h2 is present but i still getting errors.
import React, { useEffect } from 'react';
import { Form, Field } from 'react-final-form';
import { useHistory, Link } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { loginUser, clearErrorMessage } from '../../redux/auth/authActions';
import Input from '../Input/Input';
import ROUTES from '../../routes/routes';
import './LoginForm.scss';
const LoginForm = () => {
const dispatch = useDispatch();
const history = useHistory();
const { loading, isLogged, errorMessage } = useSelector(state => state.auth);
useEffect(() => {
if (isLogged) {
history.push('/');
}
return () => {
dispatch(clearErrorMessage());
};
}, [dispatch, history, isLogged]);
const handleSubmitLoginForm = values => {
if (!loading) {
dispatch(loginUser(values));
}
};
const validate = ({ password }) => {
const errors = {};
if (!password) {
errors.password = 'Enter password!';
}
return errors;
};
return (
<article className="login-form-wrapper">
<h2>SIGN IN</h2>
<Form onSubmit={handleSubmitLoginForm} validate={validate}>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit} autoComplete="off" className="login-form">
<div className="login-form__field">
<Field name="email" component={Input} type="email" label="E-mail" />
</div>
<div className="login-form__buttons">
<button type="submit" className={loading ? 'button-disabled' : ''}>
Sign in
</button>
</div>
</form>
)}
</Form>
</article>
);
};
export default LoginForm;
I am open for any advices :)
First of all, I am not recommending using shallow in your tests and here is a great article why.
I also recommend to check out react-testing-library instead of Enzyme as it is much nicer to use.
Now, to answer your question. You are using here hooks for redux and react-router, so you need to provide the necessary data to your componenent in test so that it can use those hooks. Let me show you an example test (that checks text in h2 element):
import React from 'react';
import { mount } from 'enzyme';
import {Provider} from 'react-redux';
import {MemoryRouter, Route} from 'react-router';
import LoginForm from './LoginForm';
describe('Login Form', () => {
it('should have SIGN IN header', () => {
const store = createStore();
const component = mount(
<Provider store={store}>
<MemoryRouter initialEntries={['/login']}>
<Route path="/:botId" component={LoginForm} />
</MemoryRouter>
</Provider>
)
expect(component.find('h2').text()).toEqual('SIGN IN');
});
});
Some explanation to this example.
I am using mount instead of shallow as I prefer to render as much as possible in my test, so that I can check if everything works together as it should.
You can see that I am not rendering my component directly, but rather it is wrapped with other components (Provider from react-redux and MemoryRouter from react-router). Why? Because I need to provide context to my Component. In this case it's redux and router context so that the data used inside exists and can be found (for example useSelector(state => state.auth) must have some state provided so that it can access auth property). If you remove any of them you would get some error saying that this context is missing - go ahead and check for yourself :).
See here for some details around testing with router context
As for testing with redux in my example there is a createStore function that I didn't define as there are a few approaches to this. One involves creating a real store that you use in your production application. This is the one that I prefer and colleague of mine wrote great article around this topic here. Other is to create some kind of mock store, like in this article. Again, I prefer the first approach, but whichever is better for you.
Answering your other question on what should you test in this example. There are multiple possibilities. It all depends mostly on you business case, but examples that I would test here includes:
Typing something into an input, clicking a button and observing that login is successful (by redirecting to new path - / in this case)
not typing a password and clicking a button - error should be shown
Checking if button class changes when it's loading
Do not dispatch login action twice, when already loading
And so on...
That's really just a tip of an iceberg on what could be written around testing, but I hope it helps and gives you a nice start to dig deeper into the topic.

React Context API with multiple values performance

I'm using the React Context API to store many global state values (around 10 and probably more will be needed) and many components are using them. Unfortunately whenever any of the values change, all components using the useContext hook have to rerender. My current solution is to use useMemo for the return value of the components and useCallback for any complex functions and inside custom hooks I have. This addresses most of my performance concerns, but having to use the useMemo and useCallback all the time is quite annoying and missing one is quite easy. Is there a more professional way to do it?
Here's an example based on my code:
GlobalStateContext.js
import React, { useState } from 'react'
const GlobalStateContext = React.createContext({ })
export const GlobalStateProvider = ({ children }) => {
const [config, setConfig] = useState({
projectInfo: ''
})
const [projectFile, setProjectFile] = useState('./test.cpp')
const [executionState, setExecutionState] = useState("NoProject")
return (
<GlobalStateContext.Provider
value={{
executionState,
config,
projectFile,
setExecutionState,
setConfig,
setProjectFile,
}}
>
{children}
</GlobalStateContext.Provider>
)
}
export default GlobalStateContext
Example.jsx
import React, { useContext } from 'react'
import GlobalStateContext from '../utils/GlobalStateContext.js'
export default Example = () => {
const {
executionState,
setExecutionState,
} = useContext(GlobalStateContext)
return useMemo(
() => (
<div>
The current execution state is: {executionState}
<br />
<button onClick={() => setExecutionState('Running')}>Running</button>
<button onClick={() => setExecutionState('Stopped')}>Stopped</button>
<button onClick={() => setExecutionState('Crashed')}>Crashed</button>
</div>
),
[
executionState,
setExecutionState,
]
)
}
Currently, this problem is unavoidable with context. There is an open RFC for context selectors to solve this, but in the meantime, some workarounds are useContextSelector and Redux, both of which prevent a subscribing component from rendering if the data it's reading did not change.

dynamically import a React Component if that file exists, otherwise show a default message

I want to conditionally import a React Component if the file exists and if not do something else. For example show a default view or message.
I tried this:
let Recipe;
try {
Recipe = require(`docs/app/Recipes/${props.componentName}`);
} catch (e) {
Recipe = () => <div>Not found</div>;
}
However the linter is complaining that I should not try to dynamicaly require a file, but use a string literal instead.
Is there a cleaner approach to to what I'm trying to achieve?
The problem is this approach is that it kills bundle optimizations and includes all files from docs/app/Recipes/ into a bundle, even if they aren't used.
A better way to write this is to use <React.Suspense> and React.lazy:
const Recipe = React.lazy(() =>
import(`docs/app/Recipes/${props.componentName}`)
.catch(() => ({ default: () => <div>Not found</div> }))
);
Which is used as:
<React.Suspense fallback={'loading...'}><Recipe/></React.Suspense>
A cleaner way to do this and avoid linter error is to have a map of possible components:
import Foo from 'docs/app/Recipes/Foo';
import Bar from 'docs/app/Recipes/Bar';
...
const componentsMap = { Foo, Bar };
...
const Recipe = componentsMap[props.componentName] || () => <div>Not found</div>;
In this case props.componentName can be validated if needed.
in fact there is. With the recent release of React v16.6.0 "lazy code splitting" was introduced. This is how it works, it makes sense to use it together with reacts' 'suspense':
import React, {lazy, Suspense} from 'react';
const Recipe = lazy(() =>import(`./docs/app/Recipes/${props.componentName}`));
function SomeComponent() {
return (
<Suspense fallback={<Spinner/>}>
<Recipe />
</Suspense>
);
}
To handle the case that the component isn't found you can use Error Boundaries. You would wrap your component with it like this:
<ErrorBoundary>
<Suspense fallback={<Spinner/>}>
<Recipe />
</Suspense>
</ErrorBoundary>
Best you read more about it directly on the react docs I linked above.
I have been troubled by this problem all afternoon, and now I have solved it:
If "../views/dev/dev.tsx" exists, import it, otherwise import '../views/not-found/not-found'
const requireCustomFile = require.context('../views/dev/', false, /dev.tsx$/);
let Dev = React.lazy(() => import('../views/not-found/not-found'));
if (requireCustomFile.keys()?.length) {
const keys: string[] = requireCustomFile.keys();
if (keys.includes('./dev.tsx')) {
const str = '/dev';
Dev = React.lazy(() => import(`../views/dev${str}`));
}
}
if dev.tsx not exit :
// ⬇️webpack report an error: Can't resolve module
import(`../views/dev/dev.tsx`))
// ⬇️webpack will not report an error until the load the module
const str = '/dev';
import(`../views/dev${str}`)

Categories

Resources