I have a form that has a two buttons on top, Sign Up / Log In, and one button on the bottom, Get Started. I am struggling to get the components to update with eachother. From the default Sign Up component, I can click on the Get Started button and the form disappears and shows the product components as it should. My issue is I cannot get the Log In component to display instead of the Sign Up.
The below is the code I am using - you will see where I have commented out two of my if statements where my intentions were trying to just have the app load whatever state its in. The last return with the ? statement seems to be tripping me up when I attempt to add the third component. I am able to correctly set the state to "loginShow", although nothing changes it just records in my console.
How can I correctly set state so it just toggles back and forth between Sign Up and Log In untill I press the Get Started button at the bottom?
clickMe = () => {
const {requestedPostsThatWeGotFromGecko} = this.state;
this.setState({ requestedPostsThatWeGotFromGecko: !requestedPostsThatWeGotFromGecko })
}
mainLogIn = () => {
// const {loginStatus} = this.state;
this.setState({ loginStatus: 'loginShow' })
}
render() {
const { requestedPostsThatWeGotFromGecko } = this.state;
const { loginStatus } = this.state;
console.log(loginStatus);
// if (loginStatus === "loginHide") return <SignUp login={() => this.clickMe()} />
if (loginStatus === "loginShow") return <Login />
// if (requestedPostsThatWeGotFromGecko.props === true) return <Cards />
return (
<div className="gecko">
{requestedPostsThatWeGotFromGecko ? (
<Cards />
): (
<SignUp login={() => this.clickMe()} />
)
}
</div>
);
}
Related
I have a React notes app that has a delete button, and a state for user confirmation of deletion.
Once user confirms, the 'isConfirmed' state is updated to true and deletes the item from MongoAtlas and removes from notes array in App.jsx.
The problem is, the note that takes the index (through notes.map() in app.jsx I'm assuming) of the deleted notes position in the array has the 'isConfirmed' state set to true without calling setState. Thus, bugging out my delete button to not work for that specific note until page refresh.
I've included relevant code from my Delete Component:
function DeletePopup(props) {
const mountedRef = useRef(); //used to stop useEffect call on first render
const [isConfirmed, setIsConfirmed] = useState(false);
const [show, setShow] = useState(false);
function confirmDelete() {
// console.log("user clicked confirm");
setIsConfirmed(true);
// console.log(isConfirmed);
handleClose();
}
useEffect(() => {
// console.log("delete useEffect() run");
if (mountedRef.current) {
props.deleteNote(isConfirmed);
}
mountedRef.current = true;
}, [isConfirmed]);
Note Component:
function Note(props) {
function deleteNote(isConfirmed) {
props.deleteNote(props.id, { title: props.title, content: props.content }, isConfirmed);
console.log("note.deleteNote ran with confirmation boolean: " + isConfirmed);
}
return <Draggable
disabled={dragDisabled}
onStop={finishDrag}
defaultPosition={{ x: props.xPos, y: props.yPos }}
>
<div className='note'>
<h1>{props.title}</h1>
<p>{props.content}</p>
<button onClick={handleClick}>
{dragDisabled ? <LockIcon /> : <LockOpenIcon />}
</button>
<EditPopup title={props.title} content={props.content} editNote={editNote} />
<DeletePopup deleteNote={deleteNote} />
</div>
</Draggable>
}
App Component:
function App() {
const [notes, setNotes] = useState([]);
function deleteNote(id, deleteNote, isConfirmed) {
if (!isConfirmed) return;
axios.post("/api/note/delete", deleteNote)
.then((res) => setNotes(() => {
return notes.filter((note, index) => {
return id !== index;
});
}))
.catch((err) => console.log(err));
}
return (
<div id="bootstrap-override">
<Header />
<CreateArea
AddNote={AddNote}
/>
{notes.map((note, index) => {
return <Note
key={index}
id={index}
title={note.title}
content={note.content}
xPos={note.xPos}
yPos={note.yPos}
deleteNote={deleteNote}
editNote={editNote}
/>
})}
<Footer />
</div>);
}
I've tried inserting log statements everywhere and can't figure out why this is happening.
I appreciate any help, Thanks!
EDIT: I changed my Notes component to use ID based on MongoAtlas Object ID and that fixed the issue. Thanks for the help!
This is because you are using the index as key.
Because of that when you delete an element you call the Array.filter then you the elements can change the index of the array which when React tries to rerender the notes and as the index changes it cannot identify the note you've deleted.
Try using a unique id (e.g. an id from the database or UUID) as a key instead.
I hope it solves your problem!
I am having trouble understanding why I cannot get images to show up in my components. I have a boolean which indicates loading, and an array that gets filled async. When I finish, I set the boolean and the component re renders. Now, I want to create a card for each item in the array and put in in a card deck (this is from react-bootstrap if that wasn't obvious). I can do this with any given boolean and array, but not with the boolean and arrays created with React.useState... Why is that and how should I go about fixing this?
I encountered this problem quite a few hours ago, and have tracked down its source to this minimal working example that still reflects what I am trying to do, but I am unsure of what to do from here.
function TestCard() {
return (
<Card>
<Card.Img src="holder.js/200x200" />
</Card>
);
}
I am trying to render the following component:
function MainComponent() {
const [boolState, setBoolState] = React.useState(false);
const [arrayState, setArrayState] = React.useState([]);
React.useEffect(() => {
setTimeout(() => {
setBoolState(true);
setArrayState([1,2,3]);
}, 2000);
});
return (
<>
{/* This works */}
{
true &&
<CardDeck>
{
[1,2,3].map(_ => {
return (
<TestCard />
);
})
}
</CardDeck>
}
{/* This doesn't, why? */}
{
boolState &&
<CardDeck>
{
arrayState.map(_ => {
return (
<TestCard />
);
})
}
</CardDeck>
}
</>
);
}
Code sandbox
I am trying to move the open state for material-ui dialog to redux to prevent it from closing when a rerender occurs, but i having trouble with the dialog when a rerender occurs. Although the state is saved in redux and the dialog does stay open whenever a rerender occurs the open state stays open but the dialog does show the open animation (fading in) which is kinda annoying.
Students.js (parent component of the modal)
const Students = ({
app: { studentsPage: { savedAddDialogOpen }},
setStudentsPageAddDialogOpen}) => {
// Create the local states
const [dialogOpen, setDialogOpen] = React.useState(savedAddDialogOpen);
const dialogOpenRef = React.useRef(savedAddDialogOpen);
// Change redux dialog open
const setReduxDialogState = () => {
setStudentsPageAddDialogOpen(dialogOpenRef.current, savedAddDialogOpen);
};
// Open add student dialog
const dialogClickOpen = () => {
setDialogOpen(true);
dialogOpenRef.current = true;
setTimeout(() => setReduxDialogState(), 300);
};
// Close add student dialog
const dialogClose = () => {
setDialogOpen(false);
dialogOpenRef.current = false;
setTimeout(() => setReduxDialogState(), 300);
};
return (
<Container>
{/* Add student modal */}
<AddStudentModal dialogOpen={dialogOpen} dialogClose={dialogClose} />
</Container>
)
}
// Set the state for this component to the global state
const mapStateToProps = (state) => ({
app: state.app,
});
AddStudentModal.js
const AddStudentModal = ({
dialogOpen, dialogClose
}) => {
return (
<Dialog
open={dialogOpen}
>
{/* Lots of stuff*/}
<DialogActions>
<Button onClick={dialogClose}>
Close dialog
</Button>
</DialogActions>
</Dialog>
)
};
I hope this should be sufficient. I tried checking if the open state is actually correct when a rerender occurs and it is correct every time but it looks like the dialog is closed at a rerender no matter what the open state is and only a few ms later actually notices that it should be opened.
Any help would be really appreciated
Edit 1: Found out it has nothing to do with the open state coming from redux, if i use open={true} it still flashes, so probably a problem with material-ui itself?
Edit 2: PrivateRoute.js
const PrivateRoute = ({
auth: { isAuthenticated, loadingAuth },
user: { loggedInUser },
component: Component,
roles,
path,
setLastPrivatePath,
...rest
}) => {
useEffect(() => {
if (path !== '/dashboard' && path !== '/profile') {
setLastPrivatePath(path);
}
// Prevent any useless errors with net line:
// eslint-disable-next-line
}, [path]);
// If we are loading the user show the preloader
if (loadingAuth) {
return <Preloader />;
}
// Return the component (depending on authentication)
return (
<Route
{...rest}
render={props =>
!isAuthenticated ? (
<Redirect to="/login" />
) : (loggedInUser && roles.some(r => loggedInUser.roles.includes(r))) ||
roles.includes('any') ? (
<Component {...props} />
) : (
<NotAuthorized />
)
}
/>
);
};
// Set the state for this component to the global state
const mapStateToProps = state => ({
auth: state.auth,
user: state.user
});
I found the problem thanks to #RyanCogswell!
For anyone having the same problem here is the cause for me and the fix:
I was passing components into the Route component like this:
<PrivateRoute
exact
path={'/dashboard/students'}
component={(props) => (
<Students {...props} selectedIndex={selectedIndexSecondary} />
)}
roles={['admin']}
/>
By doing it this way i could pass props through my privateRoute function but this would also happen if you send the component this way in a normal Route component
Solution for me is just moving selectedIndexSecondary to redux and sending the component the normal way it prevented the re-mounting.
So just doing it like this will prevent this from happening.
<PrivateRoute
exact
path={'/dashboard/students'}
component={Students}
roles={['admin']}
/>
Also this will solve the localstates in your components from resseting to the default value. So for me it fixed two problems!
I have multiple layers of React components for getting an embed from a music service API, including a higher-order component that hits the API to populate the embed. My problem is that my lowest-level child component won't change state. I basically want the populated embed (lowest level component) to display an album cover, which disappears after clicking it (revealing an iframe), and whose state remains stable barring any change in props higher up (by the time this component is revealed, there should be no other state changes aside from focus higher up). Here's the code:
Parent:
return (
/*...*/
<Embed
embed={this.props.attributes.embed}
cb={updateEmbed}
/>
/*...*/
First child ( above):
render() {
const {embed, className, cb} = this.props;
const {error, errorType} = this.state;
const WithAPIEmbed = withAPI( Embed );
/*...*/
return <WithAPIEmbed
embed={embed[0]}
className={className}
cb={cb}
/>;
/*...*/
withAPI:
/*...*/
componentWillMount() {
this.setState( {fetching: true} );
}
componentDidMount() {
const {embed} = this.props;
if ( ! embed.loaded ) {
this.fetchData();
} else {
this.setState( {
fetching: false,
error: false,
} );
}
}
fetchData() {
/*... some API stuff, which calls the callback in the top level parent (cb()) setting the embed prop when the promise resolves -- this works just fine ...*/
}
render() {
const {embed, className} = this.props;
const {fetching, error, errorType} = this.state;
if ( fetching ) {
/* Return some spinner/placeholder stuff */
}
if ( error ) {
/* Return some error stuff */
}
return (
<WrappedComponent
{...this.props}
embed={embed}
/>
)
}
And finally the last child I'm interested in:
constructor() {
super( ...arguments );
this.state = {
showCover: true,
};
}
render() {
const {embed, setFocus, className} = this.props;
const {showCover} = this.state;
if ( showCover ) {
return [
<div key="cover-image" className={classnames( className )}>
<figure className='cover-art'>
<img src={embed.coverArt} alt={__( 'Embed cover image' )}/>
<i onClick={() => {
this.setState( {showCover: false,} );
}}>{icon}</i> // <-- Play icon referenced below.
</figure>
</div>,
]
}
return [
<div key="embed" className={className}>
<EmbedSandbox
html={iframeHtml}
type={embed.embedType}
onFocus={() => setFocus()}
/>
</div>,
];
}
My issue is that clicking the play icon should clear the album cover and reveal the iframe embed, but even though the click is registering, the state never changes (or does and then changes back). I believe it's because a higher-level component is mounting/unmounting and reinstantiating this component with its default state. I could move this state up the tree or use something like Flux, but I really feel I shouldn't need to do that, and that there's something fundamental I'm missing here.
The problem is that const WithAPIEmbed = withAPI( Embed ); is inside the render method. This creates a fresh WithAPIEmbed object on each render, which will be remounted, clearing any state below. Lifting it out of the class definition makes it stable and fixes the problem.
I'm new to react native.
My screen contains 5 buttons, each one opens the same <Modal>, but the <View> inside it will change depending on the button clicked.
If I click the first button, a text input will be shown into the modal.
If I click the second button, a switch will be shown into the modal.
I've made a modal component (Modal.tsx) :
export default class Modal extends Component {
constructor(props) {
super(props)
}
public render() {
return (
<View style={style.modal} >
{this.props.children}
<View>
)
};
}
// Specific modal implementation with TextInput
const ModalWithTextInput = props => (
<Modal>
<TextInput
value={props.someValue}
/>
<Modal>
)
// Specific modal implementation with Switch
const ModalWithSwitch = props => (
<Modal>
<Switch
value={props.someValue}
/>
<Modal>
)
And now in my 5-button-screen (ButtonsScreen.tsx), I open the right modal depending on the button clicked :
openTextModal = () => {
this.setState({ modalType: 'text' });
}
openSwitchModal = () => {
this.setState({ modalType: 'switch' });
}
These functions are called with, for example, onPress={this.openTextModal}
Finally, I render the modal, to be able to do something like :
<View>
{this.renderModal(modalType)}
</View>
As this :
renderModal = (type) => {
if (type === 'text') {
return <ModalWithTextInput someValue="default text" />
}
if (type === 'switch') {
return <ModalWithSwitch someValue={false}/>
}
}
When I try to open a modal with onPress={this.openTextModal}, nothing happens (no error, no warning).
Anyone can please help ? Thanks.
You need to extract modalType from state, in the render method of your component that displays the Modal.
Clicking the button, only set's state, you need to handle state in the component in order to trigger a refresh. A refresh of the render method will render your Modal changes; React 101.
render() {
const { modalType } = this.state;
return (
<View>
{this.renderModal(modalType)}
</View>
);
}
Based on the fact that this is pretty much my code from your other question. I strongly suggest you take a step back, learn the basic's of React rather than just asking people to piece together solutions which you do not understand. Otherwise the result is you learn very little and have code that you do not understand.