setState is not defined with reactJS - javascript

I'm working with reactJS and I am trying to use setState so that i can use that state to determine which way the graph needs to rotate but I get an error saying 'setState' is not defined no-undef. Do I need to have a constructor to initialize the state?
Code:
class App extends React.Component {
render() {
const {rotate, setRotate} = setState(false)
return (
<div className="custom-container">
<Tree
data={data}
height={600}
width={1000}
svgProps={{transform: 'rotate(${rotate ? "90" : "0")'}}
/>
<button onClick={() => setRotate(!rotate)}>Rotate</button>
</div>
);
}
}

If you are trying to use, "useState" instead of setState,
'useState' is used for having a state in functional components, you will have to convert class component to function component as below and then use 'useState'
export default function App() {
const [rotate, setRotate] = React.useState(false)
return (
<div className="custom-container">
<Tree
data={data}
height={600}
width={1000}
svgProps={{transform: 'rotate(${rotate ? "90" : "0")'}}
/>
<button onClick={() => setRotate(!rotate)}>Rotate</button>
</div>
);
}

You could also try this:
class App extends React.Component {
state = {
rotate: false
};
render() {
return (
<div className="custom-container">
<Tree
data={data}
height={600}
width={1000}
svgProps={{transform: 'rotate(${rotate ? "90" : "0")'}}
/>
<button onClick={() => this.setState({ rotate: !this.state.rotate})}>Rotate</button>
</div>
);
}
}

I think you are trying to use useState();
Make sure you import useState at the top first and also use function component instead of the class component :
import React, {useState} from 'react';
function App() {
const [rotate, setRotate] = useState(false)
return (
<div className="custom-container">
<Tree
data={data}
height={600}
width={1000}
svgProps={{transform: 'rotate(${rotate ? "90" : "0")'}}
/>
<button onClick={() => setRotate(!rotate)}>Rotate</button>
</div>
);

Related

Passing data from child to parent component / React hooks

Can someone tell me if there is a way to pass the dark variable from Navbar component to the App component here is a little part from my Navbar component which contains the state:
function Navbar({search, handleSearch, region, handleChange, number}){
const [dark , setDark] = useState(false)
function handlThem(){
setDark(prevThem=> !prevThem )
}
return(
<div className="navbar" style={ dark ? {backgroundColor : "#333"} : null}>
)
}
I want to pass dark here in the App component to change and use it to change it's class or toggle to change the background like this style={ dark ? {backgroundColor : "#333"}
the App component :
function App() {
return (
<div className="App">
<Body />
</div>
);
}
This is a good use case for React Context. I'm providing an example using hooks API. You can create a context then use the values (state and state setter) in any of the components you wrap with the provider.
const ThemeContext = React.createContext();
function App() {
const [dark , setDark] = React.useState(false);
return (
<ThemeContext.Provider value={{ dark, setDark }}>
<Body />
</ThemeContext.Provider>
);
}
function Navbar() {
const value = React.useContext(ThemeContext);
return (
<div>Navbar<button onClick={() => value.setDark(true)}>Change to Dark</button></div>
);
}
function Body() {
const value = React.useContext(ThemeContext);
return (
<div style={ value.dark ? {backgroundColor : "#333"} : null}>
<Navbar />
<div>Rest of the body</div>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root" />
Try this
App.js
function App() {
function getTheme(themeValue) {
console.log(themeValue);
}
return (
<div className="App">
<Body getTheme={getTheme}/>
</div>
);
}
Navbar.js
function Navbar({search, handleSearch, region, handleChange, number, getTheme}){
const [dark , setDark] = useState(false)
function handlThem(){
const theme = !dart;
setDark(prevThem=> theme )
getTheme(theme);
}
return(
<div className="navbar" style={ dark ? {backgroundColor : "#333"} : null}>
)
}
As I passed to body, you pass to Navbar component, you didn't post body component that's why passed to body component and accessed in Navbar. you can change as per your requirement.

React Multiple Loading state

I'm trying to add a loading icon for each card while the data is fetching from the API. Is there another way to keep this DRY? ... to avoid repeating myself?
{isLoading ? (<Loader />) : (
<div className="module card">{sales_numbers}</div>
)}
{isLoading ? (<Loader />) : (
<div className="module card">{accounts_numbers}</div>
)}
{isLoading ? (<Loader />) : (
<div className="module card">{users_numbers}</div>
)}
You can use with an array your data and calling .map() for displaying values. You can store those items in useState() hook for function component or this.state in a class component.
Try the following if you have a function component:
const [values] = useState([sales_numbers, account_numbers, users_numbers]);
return <>
{
values.map(e => isLoading ?
<Loader /> :
<div className="module card">{e}</div>
)
}
</>
In class component you need to store within this.state like this:
constructor(props) {
super(props);
this.state = {
values: [sales_numbers, account_numbers, users_numbers]
}
}
render() {
return <>
{
this.state.values.map(e => isLoading ?
<Loader /> :
<div className="module card">{e}</div>
)
}
</>
}
I hope this helps!
Try building a component that manage loading state by itself.
This way you can have something like
export default ({data}) => (
data ? <div className="module card">{data}</div> : <Loader />
)
Your only concern is to pass data prop for each entity.

ReactDOM.render doesn't update component on props change

I have create a very small app to demonstrate my query.
Below shown code has the functionality where the component is dynamically added to DOM using ReactDOM.render and this component carries a prop called title, but when I update the title of the parent component ( in state ) the DynamicComponent doesn't update.
import React from 'react';
import ReactDOM from 'react-dom';
const DynamicComponent = (props) => {
return (
<div style={{ 'border': '2px dotted green' }} >Dynamic Component : {props.title}</div>
)
}
class App extends React.Component {
state = {
title: 'Iam Title'
}
addBlock = () => {
return ReactDOM.render(<DynamicComponent title={this.state.title} />, document.getElementById('dynamiccomponents'))
}
render() {
return (
<div>
<div>Value in state: <b>{this.state.title}</b></div>
<p><b><DynamicComponent /></b> Added Initially</p>
<DynamicComponent title={this.state.title} />
<br />
<p><b><DynamicComponent /></b> Added By ReactDOM.render will be shown below: </p>
<div id="dynamiccomponents"></div>
<button onClick={this.addBlock} >Click to Dynamic Component</button>
<button onClick={() => this.setState({ title: `Update Title` })} >Update Title</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
The first button is used to added the DynamicComponent, works fine as expected.
The Second button is used to update the title in state, now the title got changed but still DynamicComponent doesn't update.
am I missing anything, how do I solve this issue, any help would be appreciated
Thanks
You could re-render the component after state change using a LifeCycle method componentDidUpdate()
import React from "react";
import ReactDOM from "react-dom";
const DynamicComponent = props => {
return (
<div style={{ border: "2px dotted green" }}>
Dynamic Component : {props.title}
</div>
);
};
class App extends React.Component {
state = {
title: "Iam Title"
};
addBlock = () => {
return ReactDOM.render(
<DynamicComponent title={this.state.title} />,
document.getElementById("dynamiccomponents")
);
};
componentDidUpdate() {
return ReactDOM.render(
<DynamicComponent title={this.state.title} />,
document.getElementById("dynamiccomponents")
);
}
render() {
return (
<div>
<div>
Value in state: <b>{this.state.title}</b>
</div>
<p>
<b><DynamicComponent /></b> Added Initially
</p>
<DynamicComponent title={this.state.title} />
<br />
<p>
<b><DynamicComponent /></b> Added By ReactDOM.render will be
shown below:{" "}
</p>
<div id='dynamiccomponents'></div>
<button onClick={this.addBlock}>Click to Dynamic Component</button>
<button onClick={() => this.setState({ title: `Update Title` })}>
Update Title
</button>
</div>
);
}
}
export default App;
This is because when you call addBlock, you are only rendering <DynamicComponent title={this.state.title} /> once to the <div id="dynamiccomopnents"></div>.
When you update the state of title by clicking the button, it re-runs your App's render function, but this.addBlock does not get run again in your render function and therefore your title does not get updated. You can verify this by clicking the button that calls this.addBlock again. It will render your component again, with the updated title.
I'd suggest you introduce some state to conditionally render your component instead of using ReactDOM.render. That way, your component gets re-rendered everytime your render method is run. Here's an example:
import React from 'react';
import ReactDOM from 'react-dom';
const DynamicComponent = (props) => {
return (
<div style={{ 'border': '2px dotted green' }} >Dynamic Component : {props.title}</div>
)
}
class App extends React.Component {
state = {
title: 'Iam Title',
showBlock: false,
}
addBlock = () => {
// this method now sets `this.state.showBlock` to true
this.setState({ showBlock: true });
}
renderBlock = () => {
// return any component you want here, you can introduce some conditional
// logic or even return nested elements, for example:
return (
<div>
<p>Dynamic Component!</p>
<DynamicComponent title={this.state.title} />
</div>
);
}
render() {
return (
<div>
<div>Value in state: <b>{this.state.title}</b></div>
<p><b><DynamicComponent /></b> Added Initially</p>
<DynamicComponent title={this.state.title} />
<br />
<p><b><DynamicComponent /></b> Added By ReactDOM.render will be shown below: </p>
{/* This will run `this.renderBlock` only if `this.state.showBlock` is true */}
{this.state.showBlock && this.renderBlock()}
<button onClick={this.addBlock} >Click to Dynamic Component</button>
<button onClick={() => this.setState({ title: `Update Title` })} >Update Title</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
ReactDOM.render renders element only once. It creates a different tree that is not connected to your first tree. That is, React doesn't keep track of all ReactDOM.renders you might have ever called and doesn't update them with data that was used to create them
If you need to render element somewhere in the DOM tree outside of your App component but you want it to be connected with your App component (so that it reacts to state changes), use ReactDOM.createPortal

How can I pass a ref to HOC that uses onClickOutside('react-onclickoutside')?

I use onClickOutside('react-onclickoutside') for my HOC and I can't pass ref for this HOC, I have something like below and an error appears:
const inputRef = useRef();
....
<SomeCompontnt
inputRef={inputRef}
items={items}
onSelect={onSelect}
value={selectedItem}
/>
....
export default onClickOutside(forwardRef(
(props, inputRef) => <MyHoc inputRef={inputRef} {...props} />)
);
....
Errors
Uncaught TypeError: Cannot read property 'isReactComponent' of
undefined
at onClickOutside.render (react-onclickoutside.es.js?7e48:325)
try this:
you can see MyHoc onClick output in OnclickoutsideDemo MyHookClick console
import React from "react";
import { render } from "react-dom";
import onClickOutside from "react-onclickoutside";
class OnclickoutsideDemo extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const options = this.state.showOptions ? (
<MyHookClick
onClick={e => console.log(e)}
handleClickOutside={() => this.setState({ showOptions: false })}
/>
) : null;
return (
<div>
<span
onClick={() =>
this.setState({ showOptions: !this.state.showOptions })
}
>
Click Me
</span>
{options}
</div>
);
}
}
const MyHoc = React.forwardRef((props, ref) => (
<div ref={ref} onClick={e => props.onClick("HaHa")}>
Click Me to see HaHa in console !
</div>
));
const MyHookClick = onClickOutside(props => (
<MyHoc ref={props.inputRef} {...props} />
));
export default OnclickoutsideDemo;
render(<OnclickoutsideDemo />, document.getElementById("root"));

How do I clear the the array of a state?

So this is my code :
import React from "react";
import Navigation from './Navigation';
import Foot from './Foot';
import MovieCard from './MovieCard';
class Favorites extends React.Component {
render() {
const { onSearch, favorites, favoriteCallback, totalFavorites, searchKeyUpdate } = this.props;
return (
<div>
<Navigation
onSearch={onSearch}
totalFavorites={totalFavorites}
searchKeyUpdate={searchKeyUpdate} />
<div className="container">
<button onClick={()=> this.clearFavorites(favorites)}> Clear all movies </button>
{(favorites.length < 1) ?
<h1 style={{ fontSize: '13px', textAlign: 'center' }}>Please mark some of the movies as favorites!</h1>
:
<ul
className="movies">
{favorites
.map(movie => (
<MovieCard
movie={movie}
key={movie.imdbID}
toggleFavorite={favoriteCallback}
favorites={favorites}
/>
))}
</ul>
}
<Foot />
</div>
</div>
);
}
}
const clearFavorites = (favorites) => {
this.setState({ favorites: [] });
}
The thing I need for the button to do is that when i click it that it clears the whole state of favorites. The clearFavorites function is used to clear everything but when I try this I get an error:
Why doesn't this clear the state of favorites?
You have two problems:
clearFavorites function is not in your class. So you should put it inside.
You are trying to clear the data inside the favorites array, which is not part of your state, using the function clearFavorites. So, first of all, you should add favorites array to your state and then you can manipulate the information. I suggest you to use the function getDerivedStateFromProps.
As others mentioned, first moving clearFavorites function into Favorites class.
Second, your favorites list is not part of state object, but instead you pull it out from this.props.favorites, so instead of using this.setState, we should just change the props value.
Third, since you're emptying the array, the parameter in your clearFavorites probably not needed? Please refer to below:
First we define a constructor to get the value from props and pass it to state in the constructor as below:
constructor(props) {
super(props);
this.state = {favorites: this.props.favorites}
}
clearFavorites = () => {
this.setState({favorites: []});
};
Then at last in your render method change to following:
const { onSearch, favoriteCallback, totalFavorites, searchKeyUpdate } = this.props;
const favorites = this.state.favorites;// Or in your ul tag, instead of using favorites, change it to this.state.favorites
You can try to move the clearFavorites into your component
import React from "react";
import Navigation from "./Navigation";
import Foot from "./Foot";
import MovieCard from "./MovieCard";
class Favorites extends React.Component {
render() {
const {
onSearch,
favorites,
favoriteCallback,
totalFavorites,
searchKeyUpdate
} = this.props;
return (
<div>
<Navigation
onSearch={onSearch}
totalFavorites={totalFavorites}
searchKeyUpdate={searchKeyUpdate}
/>
<div className="container">
<button onClick={() => this.clearFavorites(favorites)}>
{" "}
Clear all movies{" "}
</button>
{favorites.length < 1 ? (
<h1 style={{ fontSize: "13px", textAlign: "center" }}>
Please mark some of the movies as favorites!
</h1>
) : (
<ul className="movies">
{favorites.map(movie => (
<MovieCard
movie={movie}
key={movie.imdbID}
toggleFavorite={favoriteCallback}
favorites={favorites}
/>
))}
</ul>
)}
<Foot />
</div>
</div>
);
}
clearFavorites = favorites => {
this.setState({ favorites: [] });
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Categories

Resources