React Multiple Loading state - javascript

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.

Related

how do you pass down an object containing props

after getting all mixed up with state, i am now trying to restructure my app in a way that might be more reflective of best practices (not sure if this is the way, advice is welcome.)
so, i have my main page, which holds 3 states: viewer,buyside,sellside
there are also three different components, one for each of those states.
i want to be able to pass the props down from the main page, through those components, to their children (i've read this is the best approach??)
main page:
//we have 3 states for the website: viewer,buyside customer, sellside customer
const [visitorType, setVisitorType] = useState('viewer');
if (visitorType == 'viewer') {
return(
<div>
<Viewer visitortype='viewer' setvisitor={()=>setVisitorType()}/>
</div>
)}
else if (visitorType =='buyside') {
return(
<div>
<Buyside visitortype='buyside' setvisitor={()=>setVisitorType()}/>
</div>
)}
else if (visitorType =='sellside') {
return(
<div>
<Sellside visitortype='sellside' setvisitor={()=>setVisitorType()}/>
</div>
)}
};
what is the best way to pass down the main page props, so that i can bring them down to any grandchildren, along with the child props?
the viewer component -UPDATED-:
const MainView = (props) => {
return(
<>
<Navbar mainprops={{props}}/>
</>
)
};
export default MainView
i was previously just passing them individually, but realized it might be better to do so as one object...
UPDATE: point taken on the syntax, but i'm wondering how i can best pass the objects
nav component (grandchild)
const Navbar = (props) => {
const {mainprops} = props.mainprops;
if (mainprops.visitortype == 'viewer') {
return(
<>
<h1>viewer navbar</h1>
</>
)}
else if (mainprops.visitortype =='buyside') {
return(
<>
<h1>buyside navbar</h1>
</>
)}
else if (mainprops.visitortype =='sellside') {
return(
<>
<h1>sellside navbar</h1>
</>
)}
};
export default Navbar;
UPDATE 2 - this works, but not sure if it is the correct way, are these still considered object literals??
viewer component:
const MainView = (props) => {
const mainprops = {...props}
return(
<>
<Navbar mainprops={mainprops}/>
</>
)
};
export default MainView
navbar component
const Navbar = (props) => {
const mainprops = {...props.mainprops};
if (mainprops.visitortype == 'viewer') {
return(
<>
<h1>viewer navbar</h1>
</>
)}
else if (mainprops.visitortype =='buyside') {
return(
<>
<h1>buyside navbar</h1>
</>
)}
else if (mainprops.visitortype =='sellside') {
return(
<>
<h1>sellside navbar</h1>
</>
)}
};
export default Navbar;
if this is correct, then is this what #amir meant?
First there are certain rules for passing props:
You never ever pass literal object as props since it will not be the same every re-render and will cause the child component to re-render too (without any new info)
You don't need to do that
<Viewer visitortype='viewer' setvisitor={()=>setVisitorType()}/>
You can:
<Viewer visitortype='viewer' setvisitor={setVisitorType}/>
since it comes from useState react make sure the setVisitorType keeps the same reference
And now for you error, you almost correct you just did a js syntax error
you should write it like this:
const MainView = (props) => {
return(
<>
<Navbar mainobj={{
visitortype:props.visitortype,
setvisitor:props.setvisitor
}}
/>
</>
)
};
export default MainView
But again you never send literal object as props
I would keep it inside a ref or state (depend if the visitor state will be change)

setState is not defined with reactJS

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>
);

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 Context Provider with multiple values being updated in several places

I am new to React and Context and am trying to use a global context provider in React.
export const GlobalDataContext = React.createContext({ user: null, textSearchList:[] });
user is updated in the same file this way:
return (
<GlobalDataContext.Provider value={{ currUser: user, textSearchList: []}}>
{children}
</GlobalDataContext.Provider>
);
I want to use the same context provider to update the textSearchList for a search bar in another component like this:
<GlobalDataContext.Provider value={{textSearchList:this.state.splitSearchList}}>
<SearchBar
value={this.state.value}
onChange={(newValue) => {
this.setState({ value: newValue });
}}
onRequestSearch={() => {
this.setSplitList(this.state.value);
}}
style={{
margin: '0 auto',
maxWidth: 800
}}
/>
{children}
</GlobalDataContext.Provider>
The above code is calling this function:
setSplitList = (searchString) =>{
var splitString = this.state.value.split(" ");
this.state.splitSearchList= splitString;
}
I can see that this is not updating the global context because it does not cause a re-rendering at the consumer which is here:
<GlobalDataContext.Consumer >
{({textSearchList}) =>
<Query query={GET_POSTS} pollInterval={500}>
{({ data, loading }) => (
loading
? <Loading />
: <div>
{data.posts.filter(function(post){
console.log(`Filtering based on this: ${textSearchList}`);
return this.textContainsStrings(post.text, textSearchList)
}).map(post => (
<div key={post._id}>
<PostBox post={post} />
</div>
))}
</div>
)}
</Query>
}
</GlobalDataContext.Consumer>
Is this approach even possible? If so, then what might I be doing wrong?

How to do conditional check twice in React component

I have a React component as shown. I am passing prop hasItems and based on this boolean value, i am showing PaymentMessage Component or showing AddItemsMessage component.
export const PayComponent = ({
hasItems
}: props) => {
return (
<Wrapper>
{hasItems ? (
<PaymentMessage />
) : (
<AddItemsMessage />
)}
<Alerts
errors={errors}
/>
</Wrapper>
);
};
This works well. Now, i need to pass another prop (paymentError). So based on this, i modify the JSX as below. I will highlight the parts i am adding by using comment section so it becomes easy to see.
export const PayComponent = ({
hasItems,
paymentError //-----> added this
}: props) => {
return (
<Wrapper>
{!paymentError ? ( //----> added this. This line of code errors out
{hasItems ? (
<PaymentMessage />
) : (
<AddItemsMessage />
)}
):( //-----> added this
<Alerts
errors={errors}
/>
) //-----> added this
</Wrapper>
);
};
Basically, i am taking one more input prop and modifying the way my JSX should look. But in this case, i am not able to add one boolean comparison one after the error. How do i make it working in this case. Any suggestions please ???
I recommend you to create a function to handle this behavior. It's easier to read and to mantain
export const PayComponent = ({
hasItems,
paymentError
}: props) => {
const RenderMessage = () => {
if (hasItems) {
if (paymentError) {
return <PaymentMessage />
}
return <AddItemsMessage />
}
return <Alerts errors={errors}/>
};
return (
<Wrapper>
<RenderMessage />
</Wrapper>
);
};

Categories

Resources