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
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)
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>
);
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.
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?
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>
);
};