Displaying component after 'if' statement in separate function (React) - javascript

I have a dropdown searchbox that I want to use to display results and I have a card component that I want to display when the dropdown value matches the value of the user the card component is for.
I have retrieved the state of the user from props and compared this with the state of the dropdown to create the function onSearchSubmit to display the card component 'ProfileCard'.
When testing the onSearchSubmit function using console.log, the if statement is true however the ProfileCard component does not display. There are no errors, I just can't see my component being displayed.
Any help will be greatly appreciated. This is my first question on here so if there is not enough information I will try and help as much as I can.
Please see code below. (I have omitted sections of the code I don't think is related to this section).
onSearchSubmit = () => {
if (this.state.selectedCategory === this.props.category) {
console.log('it works')
return ( <ProfileCard/> );
} else {
console.log('not working')
}
}
render() {
return (<div>
<button onClick = {
this.onSearchSubmit
}> Search</button>
</div>
)
}
Thanks,
John

You are using conditional rendering wrong way. Put the component in the render and add condition there based on some variable.
onSearchSubmit = () => {
if (this.state.selectedCategory === this.props.category) {
console.log('it works')
this.setState({
renderProfileCard: true
})
} else { console.log('not working') }
}
render() {
return (
<div>
<button onClick={this.onSearchSubmit}>Search</button>
{this.state.renderProfileCard && <ProfileCard />}
</div>
)
}
Add renderProfile flag to your state.
this.state = {
renderProfileCard: false
}

You should return the component inside the render hook itself:
render() {
return (
<div>
<button onClick={this.onSearchSubmit}>Search</button>
{
this.state.selectedCategory === this.props.category &&
<ProfileCard /> || null
}
</div>
)
}
If you want to render from outside, the I would suggest you to look at my another post most efficient way of rendering JSX element.

Related

Issues rendering the right dashboard UI user based on roles

I am trying to render UI in my project based on selected roles (brands, agency, influencer) on click. However, the logic that I am putting together is not loading the right UI and I don't quiet understand why not.
I have tried moving the role and setRole to the top component and passed the props down to the child components that read role and updated it via setRole so that I can have the state to be available in two places.
I also set a logic that should display components based on if the role equals the value of the buttons.
What happened was the components weren't loading upon clicking the function that handles click. However, logging out to the console if the role equals the value of the clicked button returns true, the right string that the logic was based on.
What I am expecting to happen is to load the component e.g: "Brands" when users click and select "brands" which is the value of the clicked button. Vise versa for the other components.
My code is as follows:
import { useState } from 'react';
import { useSession } from 'next-auth/react';
import Brands from './Brands';
import Agency from './Agency';
import CreatorsDash from './CreatorsDashboard';
export default function FirstPageModal({ role: userRole }) {
const [role, setRole] = useState(userRole);
const { data: session } = useSession();
const handleClick = (e) => {
e.preventDefault();
let buttonValue = e.target.value;
const clickedRole = role?.map((user) => {
let { role } = user;
if (buttonValue) {
userRole = { role: buttonValue };
}
return { userRole };
});
console.log(clickedRole); //Returns an array
clickedRole.map((item) => {
const { role } = item.userRole;
console.log(role); //Returns string ("agency" / "brands" / "Influencer")
if (session && role === 'brands') {
console.log(role); //This logs "brands" as expected but doesn't return the component
// return <Brands session={session} role={role} />;
} else if (session && role === 'agency') {
return <Agency session={session} role={role} />;
} else if (session && role === 'Influencer') {
return <CreatorsDash session={session} role={role} />;
} else {
console.log('Select a role');
}
});
};
return (
<>
<div className="">
<button type="button" className="" onClick={handleClick} value="agency">
As an Agency
</button>
<button type="button" className="" onClick={handleClick} value="brands">
As a Brand
</button>
<button
type="button"
className=""
onClick={handleClick}
value="Influencer"
>
As an Influencer
</button>
</div>
</>
);
}
Returning a component from an onClick handler doesn't automatically render the component. One thing you could do is to keep track of the role in the state and then put the <Brands /> <Agency/> and <CreatorsDash /> components in the render function and dynamically show/hide them like {role === "brands" && <Brands />. This can also be done with css, although the benefits of this are not so clear,.
Side note, it is very helpful to post a codepen with your code, especially as your code gets more complicated

React : How to Show Message if there is no records

I am working on project in ReactJS, I am fetching data from server through API. I did some search filtration, I want to display message if there is no records available? I am beginner to ReactJS and don't have much knowledge related to ReactJS.
Code:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
Item: 5,
skip: 0
}
this.handleClick = this.handleClick.bind(this);
}
urlParams() {
return `http://localhost:3001/meetups?filter[limit]=${(this.state.Item)}&&filter[skip]=${this.state.skip}`
}
handleClick() {
this.setState({skip: this.state.skip + 1})
}
render() {
return (
<div>
<a href={this.urlParams()}>Example link</a>
<pre>{this.urlParams()}</pre>
<button onClick={this.handleClick}>Change link</button>
</div>
)
}
}
ReactDOM.render(<Example/>, document.querySelector('div#my-example' ))
You can check when you get the data back and set an error if no data:
getData(){
const {Item,skip}=this.state;
axios.get(`http://localhost:8001/parties?filter[limit]=${Item}&&filter[skip]=${skip}`)
.then(response=>{
console.log(response.data);
if (!response.data.length) {
this.setState({noData: true})
} else {
this.setState({
data:response.data, noData: false
})
}
})
}
Then in your render function:
render() {
if (this.state.noData) {
return <p>No Data was returned!</p>;
}
...
You could check for the data before render the component and return another component if you don't have any. Using expressions such This example ‘’’ { doIHaveData ? < Component1 /> : < Component2 />} ‘’’
Where Component1 has your functionality and Component2 return a message or a spinner loader whatever you want .
I hope it helps!
When you are checking for the empty array you could check for array type and length. I would personally do something like
{Array.isArray(array) && array.length === 0 ? <component1/> : <component2/>}
You can make use of conditional rendering!
render(){
const filteredItems = this.getDataItems(this.state.filtered);
const dataItems = this.getDataItems(this.state.data);
if(dataItems){
return(
<div>Your Message</div>
)
}
else{
//Your normal code
}
}
I think a nice way would be to make your back end return a error message when there are no records to display.
You could use .catch after your axios.get().then() function to see if there are any errors returned by back end and then display it to the user.
Look: What is the proper REST response code for a valid request but an empty data?
You can add your message as another conditional wherever you'd like:
{this.state.filtered.length === 0 && (
<div>Your Message</div>
)}
Or if you're trying to add a message for getting no results at all, then:
{this.state.allData.length === 0 && (
<div>Your Message</div>
)}

Load the component when all data has ready

I'm using React with Redux.
In this example I have my class with mapStateToProps and mapDispatchToProps
class EnigmaPage extends Component {
constructor(props){
super(props);
}
componentDidMount() {
this.props.authCheckState();
}
readUserData() {
this.props.loadLevel(this.props.userId);
}
render(){
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
<LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
</div>
</div>
</div>
)
}
}
const mapDispatchToProps = dispatch => {
return {
authCheckState: () => dispatch(actions.authCheckState()),
getLevel: () => dispatch(actions.getLevel()),
loadLevel:(id) => dispatch(actions.loadLevel(id))
};
}
const mapStateToProps = state => {
return {
userId:state.auth.user,
level:state.level.level
}
}
I wanna push to my component LevelInfo the values difficulty and level but these 2 data arrive from getLevel() that is an http request with delay.
The page loads before receiving all the data from the http call.
I'm looking a way to wait to load the component LevelInfo or reload the component when the data are all ready.
You need to tell your component that will wait for the data needed to render your Level component, so into your EnigmaPage component write as follow
render(){
const { level } = this.props;
if (!level) { return <LoadingComponentSpinner />; } // this will render only when level attr is not set, otherwise will render your `LevelInfo` component
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
<LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
</div>
</div>
</div>
)
}
I hope it can help you.
We don't make our components wait, we let the initial rendering happens but render the target component with a conditional expression.
render() {
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
{this.props.level && (
<LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
)}
</div>
</div>
</div>
);
}
So, here we are checking if this.props.level is defined. When it is undefined React does not render anything, after getting the data LevelInfo component is rendered. You can change conditional rendering logic. You can render a Loading component maybe instead of nothing. But, at the end of the day, you will render your component conditionally.
try to use conditional rendering:
isDataReady() {
if(this.props.level.difficulty && this.props.level.level)
return true;
return false;
}
render(){
return (
<div className={classes.EnigmaPage}>
<div className={classes.Header}>
<div>
{this.isDataReady() ? <LevelInfo
difficulty={this.props.level.difficulty}
level={this.props.level.level}
readUserData={this.readUserData()}
/>
: <div> </div>}
</div>
</div>
</div>
);
}
in case your data is not ready you can display anything you want, for simplicity here I just added an empty <div>.
Johuder Gonzalez has talked about the Spinner. The Material UI has a lot of examples, which is easy to apply. Please look the followings.
Material UI Progress

React-Quill : how to dynamically replace a text while typing, without loosing focus and keep the cursor at the correct place

This question is specificcally dedicated to react-quill.
What I want to do : replace some text in a react-quill componement by an other while typing. For example: #s1 will be replace by something like productname(1eq, 100mg, 10ml). Be aware, I don't want to use JQuery.
So far I did it like this (code below), but once the modification is made, I lose the focus and don't know how to get it back. More over the console does throw an error of type "The given range isn't in document."
So basically, it works, but the user has to click in the component to continue typing and if I have more than one react-quill component in the page it will scroll to the firt react-quill object (which is not conveniant when working on the last one).
class Procedure extends Component {
constructor(props){
super(props);
this.state={
content='';
}
}
replaceTagByText(value){
return value.replace(/#(\w+)\s/, myNewValue );
}
handleProcedureContentChange = (value) => {
let newValue = replaceTagByText(value);
this.setState({content:newValue});
};
render() {
return (
<div>
<div style={styles.title}>My title</div>
<ReactQuill
key={"procedure_RTE_" + this.props.experimentId}
value={this.state.content}
modules={modules}
formats={formats}
onChange={this.handleProcedureContentChange}
/>
</div>
);
}
}
Please note that the code is over simplified, I use redux to serve my data, but the general idea is here.
The question is : how to properly replace a text in a react-quill while typing, without loosing focus and keep the cursor at the correct place.
If finally find a solution. The idea is to keep track of the editor and just set the right setSelection. The updated code is bellow.
class Procedure extends Component {
constructor(props){
super(props);
this.state={
content='';
}
}
replaceTagByText(value){
return value.replace(/#(\w+)\s/, myNewValue );
}
//Modification of the code is there
handleProcedureContentChange = (value) => {
let newValueObject = replaceTagByText(value);
this.setState({content:newValueObject.value});
quillRef = this.refs.myQuillRef.getEditor();
//I need that settimeout to do it after the update of my redux logic
setTimeout(() => {
quillRef.focus();
quillRef.setSelection(newValueObject.indexAfterInsertion, 0, myQuillRef);
}, 500);
};
render() {
let myRef = (el) => this.myQuillRef = el;
return (
<div>
<div style={styles.title}>My title</div>
<ReactQuill
key={"procedure_RTE_" + this.props.experimentId}
ref={myRef}
value={this.state.content}
modules={modules}
formats={formats}
onChange={()this.handleProcedureContentChange}
/>
</div>
);
}
}

React: How can I render multiple menus with map() without causing a re-render?

I have a component called <SiteMenu />. Inside of my render function I have these three lines:
render() {
{ this.renderPrimaryMenu() }
{ secondaryMenuContents && this.renderSecondaryMenu() }
{ this.renderAdditional() }
}
Each of those have a corresponding function that maps through results and creates menus as unordered list. A boiled-down version:
renderAdditional() {
const { secondaryMenuContents } = this.props;
if (!secondaryMenuContents) { return false; }
const additional = filter(secondaryMenuContents.sections, { additional: true });
if (!additional || additional.length === 0) { return false; }
const links = additional.map(
(link, index) => {
return (
<Link
key={ `${index}-${link.link}` }
to: link.link
>
{ link.text }
</Link>
);
}
);
return (
<nav className={ styles['nav--additional'] }>
<Responsive>
<h3 className={ styles.h3 }>{ Lang.additionalSection.title }</h3>
<menu className={ styles['menu--additional'] }>
{ links }
</menu>
</Responsive>
</nav>
);
}
Each time one of these lists is rendered it re-renders the entire component. One of the menus uses static JSON (renderPrimaryMenu()) while the other two depend on data in two separate calls from an API, so that data doesn’t always come in at the same time.
Any suggestions for ensuring a single render OR, even better, having the first static menu (which fades in and re-fades in with every render) display and the other two render when they’re ready without causing the first menu to re-render?
Appreciate any help I can get!
I suggest you to separate these three components.
And use shouldComponentUpdate() to ensure whether to rerender the component.
This is the pseudo-code:
class PrimaryMenu extends Component {
shouldComponentUpdate() {
// if data is the same, return false
// else return true
}
render() {
return (
...
)
}
}
class SecondaryContent extends Component {
// same logic as PrimaryMenu
}
class Additional extends Component {
// same logic as PrimaryMenu
}
class SiteMenu extends Component {
render() {
return (
<PrimaryMenu/>
<SecondaryContent/>
<Additional/>
)
}
}
So with this setup, you can control the re-render time at each Menu.
or try PureComponent, it exists to reduce re-rendering stuff.
import React, { PureComponent } from 'react';
class Additional extends PureComponent {
}
More info
https://reactjs.org/docs/react-api.html#reactpurecomponent

Categories

Resources