Reactjs set state in props array - reactjs

I'm New to React.js, trying to build form validation.
There is Btn component.
const MyBtn = ({opts}) => {
if(!opts) {
opts = {};
}
return (
<button {...opts}}>{opts.value}</button>
)
}
There is Home.js which is a login page. I don't add other codes that you might not need for this problem.
class Home extends Component {
state = {
formValid: false
}
regexCheck = {
disabled : {this.state.formValid},
value: "LogIn",
type: "submit"
}
render(){
return(
<MyBtn opts={this.regexCheck}/>
)
}
}
//output that I'm trying to get
<button type="submit" disabled={this.state.formValid}>LogIn</button>
I get an error that I can't call {this.state.formValid} in the props. since this is a reserved word. (Sorry for my bad English.) I feel like I'm totally wrong.
Anyway, I thought I can use getDerivedStateFromProps method for this...? so I added this in Home.js but didnt work.
static getDerivedStateFromProps(nextProps, prevState){
if(prevState.formValid !== nextProps.formValid){
return { formValid: nextProps.formValid };
}
return null;
}
As you can see, I'm still confused by props and state. ahhhh but I still gotta keep doing this project. Please give me any idea to solve this.

This is invalid syntax:
disabled : {this.state.formValid},
You simply want:
disabled : this.state.formValid,

Related

what is the best way to share the state outside same component in react

I have encountered a problem and I am new to react. I wanted to find what is the best way to share react state outside of the same component for updating input value
function async callAjax(makeAjaxRequest){
//some ajax call
updateState();
return false;
}
function updateState() {
//I want to update state here from component and from outside
// component as well i.e call from callAjax function
//I wanted to submit form after state update, Can I pass formRef to
//chaining functions to submit or is there any better way?
}
export class test extends React.Component<testProps> {
constructor(props) {
super(props);
this.state = {
action: ''
}
AjaxRequest = await callAjax(
this.props.makeAjaxRequest
);
updateState();
render() {
<form>
<input type="hidden" value={this.state.action} />
</form>
}
}
I have done research around this found some like react sharedContext(useContext) but useContext is mostly used between different components for sharing data but I wanted inside single component. Can anyone help find best way to solve this problem?
Thanks in advance.
I think you shouldn't update the state of a component outside of the component as this may lead to problems. If you must have updateState outside of the component I think you can add callback which will be run when needed.
function async callAjax(makeAjaxRequest, stateCallback ){
updateState( stateCallback );
return false;
}
function updateState( stateCallback ) {
const newValue = 123
stateCallback( newValue )
}
export class Test extends React.Component<TestProps> {
constructor(props) {
super(props);
this.state = {
action: ''
}
}
AjaxRequest = await callAjax(
this.props.makeAjaxRequest,
( newValue ) => this.setState( newValue )
);
render() {
<form>
<input type="hidden" value={this.state.action} />
</form>
}
}
You can also find concept of Redux interesting.

Removing a cookie popup on button click in React isn't working

I've got a very simple component where I'd like to render a 'cookiePopup' based on whether the state 'cookieShow' is true or false. I'd like to switch the state to false by clicking the 'cookie close container', thereby removing the component. This is done through a ternary operator which if 'cookieShow' is true will render the component, if it's false it will return 'null'.
However, whenever the page loads, the cookiePopup doesn't show. I think this is due to my ternary operator, as it feels like this is something straightforward but can't figure it out.
Here's my code:
class CookiePopup extends Component {
constructor(props) {
super(props)
this.state = {
cookieShow: true
}
this.handleClick = this.handleClick.bind(this)
this.showCookiePopup = this.showCookiePopup.bind(this)
this.removeCookiePopup = this.removeCookiePopup.bind(this)
}
handleClick() {
this.setState(state => ({
cookieShow: false
}));
}
showCookiePopup() {
return (
<div className="cookie-popup-container">
<div onClick={this.handleClick} className="cookie-close-container">
<span className="cookie-close-span-1"></span>
<span className="cookie-close-span-2"></span>
</div>
<div className="cookie-text-container">
<p className="cookie-text">By using our website, you agree to our <a class="cookie-link" href="/cookies" target="_self">cookies</a> policy.</p>
</div>
</div>
)
}
removeCookiePopup() {
return (
null
)
}
render() {
return (
<div>
{ this.state.cookieShow ? this.showCookiePopup : this.removeCookiePopup }
</div>
)
}
}
Not sure what I'm doing wrong, but any advice would be appreciated! Thank you.
Try this
{ this.state.cookieShow ? this.showCookiePopup() : this.removeCookiePopup() }
Concerning the render problem, I would say it's because you do not execute the functions in your ternary, try to add the parenthesis like so :
{ this.state.cookieShow ? this.showCookiePopup() : this.removeCookiePopup() }
The following ain't causing your problems, but you could better code wise :
You don't need to bind your methods in the constructor if you use arrow functions, like so :
removeCookiePopup = () => { // TODO }
I'm not sure about the way you wrote your setState, if someone else can confirm it's a valid way to write it ? Anyway, usually we write it this way :
this.setState({ cookieShow: false });
Or even better, you can use the previous value of the state, as React states it's the way to go (https://reactjs.org/docs/state-and-lifecycle.html), like this :
this.setState((previousState) => ({ cookieShow: !previousState.cookieShow}) );
which will toggle your modal between true and false, meaning you only need one method to open and close it.

onClick for Cruise List Heading using React

At the moment I am trying to do a website on cruise ships using React in my spare time.
I have a working version on my Reviews branch, here https://github.com/RobertWSON/Personal-ship-project/tree/reviews.
However I am wanting to change how the Cruise Lines Page is displayed.
I would like to have Cruise Line Headings across the page.
When a Cruise Line Heading is clicked it expands to show a List of Ships for that Cruise Line and if you click again, it collapses to show just the Cruise Line Heading.
At the moment I am a bit confused, as to how I can make this work and I have not got it working just yet.
I have been working on this, on a different branch called robs-shipslist-under-cruiselines: here https://github.com/RobertWSON/Personal-ship-project/tree/robs-shipslist-under-cruiselines .
I have components called CruiseListHeader.jsx and ListofShips.jsx.
Just wondering if anyone can give me any advice on whether it's possible to do a ternary operator for this handleClick, that I have in my CruiseListHeader component?
It seems to me that the code inside my handleClick function is the code that causes the errors.
I think my state for opening and closing the ShipsList, so that's OpenshipsList and CloseshipsList, needs to be handled better.
How can I better deal with this?
Does anyone have any ideas that may help me solve this problem and make it work.
The following code is from my CruiseListHeader component
import React from 'react'
import {getCruiseLines } from '../api/api';
class CruiseListHeader extends React.Component {
constructor(props) {
super(props)
//setting intial state for cruise heading and shipsList and initialize cruiseHeaders as an empty array
this.state = {
cruiseHeaders: [],
shipsList: {isOpen:false}
}
//binding methods for Cruise Line Headers and Handle Click Function
this.setUpCruiseLines = this.setUpCruiseLines.bind(this),
this.handleClick = this.handleClick.bind(this)
}
componentDidMount() {
console.log('cdm')
this.setUpCruiseLines()
}
setUpCruiseLines() {
console.log('getcruiselines')
getCruiseLines()
.then(res => {
this.setState({
cruiseHeaders: res
})
})
}
/* There will be Headings for all the Cruise Lines.
When a Cruise Line Heading is clicked, it goes to ListofShips Component and the Ships List opens up for that Heading.
When user clicks on a Cruise Line Heading, when a Ships List is open, the Ships List Collapses.*/
handleClick(event) {
// Maybe do a ternary operator here before open and close functions
this.state.shipsList === isOpen ? OpenShipsList : CloseshipsList
OpenshipsList(event) {
this.setState = {shipsList: {isOpen:true}}
return
<div>
<ListofShips/>
</div>
}
CloseshipsList(event) {
this.setState = {shipsList: {isOpen: false}}
render()
}
}
// This renders at the start when the page loads and also when you close a list
render() {
return (
<React.Fragment>
<h3><button onClick = {this.handleClick}>{ship.cruise_line}</button></h3>
</React.Fragment>
)
}
}
export default CruiseListHeader
At the moment, when I do a yarn dev I am getting the following error
ERROR in ./client/components/CruiseListHeader.jsx Module build failed:
SyntaxError: Unexpected token, expected ; (42:29)
I would like to get rid of this error and display the page like I have described above.
As a beginning, to set isOpen correctly on the state, modify the onClick function handler as this:
handleClick(event) {
// this handleClick function should only handle the `isOpen` value in the state.
// Any renders supposibly to be made on the `render` method instead.
this.setState(prevState => ({
shipsList: {
isOpen: !prevState.shipsList.isOpen, //will reverse the prevState of isOpen.
}
}));
}
Now, Going to your render, we can handle the way you renderthe component that depends on the this.state.shipsList.isOpen this way:
render() {
//destructive declaration for isOpen from inside the shipsList in the state.
const { shipsList: { isOpen } } = this.state;
return (
<React.Fragment>
<h3>
<button onClick={this.handleClick}>
{ship.cruise_line}
</button>
</h3>
{
// Usually modals are shown at the bottom of the render return.
// it's better to use tenary `val ? component : null` rather than: (val && component)
// React accepts a component, or a null as return value, the second will return false if val was false.
isOpen ? <OpenShipsList /> : null
}
</React.Fragment>
)
}
PS: Please follow the comments inside the code above of each line, they should be enough illustrating what happened, if something was ambiguos, just let me know.
Hard to tell with the indentations, but is this.state.shipsList === isOpen ? OpenShipsList : CloseshipsList supposed to really be this.state.shipsList.isOpen ? OpenShipsList() : CloseshipsList();? Note that isOpen is a property of state.shipsList, and then the parens to invoke the calls to open/close the list, and also the semi-colon to end the line.
I think you probably really want your handleClick to simply toggle the open state and then use that state value to selectively render the list.
const handleClick = event => this.setState(
prevState => ({ shipsList: {isOpen: !prevState.shipsList.isOpen} })
);
render() {
const { shipsList: { isOpen } } = this.state;
return (
<Fragment>
{isOpen && <ListofShips />}
<h3>
<button onClick={this.handleClick}>{ship.cruise_line}</button>
</h3>
</Fragment>
)
}

event bubbling in react

I am doing a React project.
However, there is one question.
I try to manage the links in the page from one place.
For example,
if page have an external link, open a new window,
if it is a link of the same site,
it tries to move from the inside.
so,
class App extends Component {
componentDidMount() {
document.addEventListener('click', (e) => {
const target = e.target;
const aLink = target.closest('a');
if (aLink && aLink.getAttribute('href')) {
const href = aLink.getAttribute('href') || '';
if (href) {
if (isExternalLink(href)) {
window.open(href);
e.preventDefault();
} else if (isOurLink(href)) {
//
} else {
//
}
}
}
});
}
:
I want to do this.
Is this an anti-pattern?
Your react component while rendering anchor links can call a service, this service can check wether a url is internal or external. Using this information you can render using something like this...
public render(): JSX.Element {
var openInNewTab = someService.IsInternal(props.someUrl);
return (
<a href={props.someUrl} target={openInNewTab ? '_blank': ''}>
</a>
);
}
As already mentioned in comments, perhaps an HOC is more suited for this.

React Router Redirect Conditional

I'm trying to make a button that only redirects the user to a new page after validation is completed correctly.
Is there a way of doing something like this?
How to I get a Route to be activated inside of a class method?
import validator from './validator';
class Example {
constructor(props) {
super(props)
this.saveAndContinue = thos.saveAndContinue.bind(this)
}
saveAndContinue () {
var valid = validator.validate(this.props.form)
if (valid) {
axios.post('/path')
<Redirect to='/other_tab'>
} else {
validator.showErrors()
}
}
render() {
<button onClick={this.saveAndContinue}>Save and Continue</button>
}
}
As discussed you should have access to the history object via this.props.history as described here.
If you look into the push function this will redirect you to any route you need.
For example:
// Use push, replace, and go to navigate around.
this.props.history.push('/home', { some: 'state' })
https://reacttraining.com/react-router/web/api/history
Like Purgatory said you can do it without using <Redirect />, but otherwise you could make your component a stateful component and then do a conditional render like so
render() {
!this.state.redirect ?
<button onClick={this.saveAndContinue}>Save and Continue</button> :
<Redirect to='/other_tab'>
}
And let the saveAndContinue() change the component state.
saveAndContinue () {
var valid = validator.validate(this.props.form)
if (valid) {
axios.post('/path')
this.setState({redirect: true});
} else {
validator.showErrors()
}
}
When the state changes it would cause a re-render and this time the <Redirect /> would be rendered.
Note: I didn't actually run this code snippet, so it may contain (hopefully minor) errors.

Resources