Loading state from parent route component in react - rect

I have this kind of situation in my react router:
root.js
<Route path="job-opportunity" component={Opportunity} >
<Route path=":opportunitySlug" />
</Route>
When I open domain.com/job-opportunity everything works fine
But when I hit child route domain.com/job-opportunity/some-slug
It doesn't load some constructor state from Opportunity component that I am using in child component.
Let me show you my Opportunity component:
class Opportunity extends React.Component{
constructor(props){
super(props);
this.loadOpportunities = this.loadOpportunities.bind(this);
this.state = {
'opportunities' : []
};
this.loadOpportunities();
}
loadOpportunities(){
$.get($('.api_router_load_opportunities').val(), function(data){
this.setState({'opportunities' : data});
}.bind(this));
}
render() {
return (
<div>
<h1>Layout!</h1>
<Content
opportunities={this.state.opportunities}
loggedUser={this.props.loggedUser}
opportunitySlug={this.props.params.opportunitySlug}
/>
</div>
)
}
}
function Content(props){
const opportunitySlug = props.opportunitySlug;
if(opportunitySlug){
return <SingleOpportunity
opportunities={props.opportunities}
loggedUser={props.loggedUser}
opportunitySlug={opportunitySlug}
/>;
}else{
return <ListOpportunity
opportunities={props.opportunities}
loggedUser={props.loggedUser}
/>;
}
}
export default Opportunity;
Basically when I hit domain.com/job-opportunity then navigate from link in browser everything works fine even in this route:
domain.com/job-opportunity/some-slug
But when I hit directly in browser domain.com/job-opportunity/some-slug
It brokes because I am rendering in SingleOpportunity this.props.opportunities[this.props.opportunitySlug].name

I found some solution:
if(this.props.opportunities.length != 0){
const opportunity = this.props.opportunities[this.props.opportunitySlug];
return (
<div key={this.props.opportunitySlug}>
<p><strong>Name:</strong> {opportunity.name}</p>
<p><strong>Description:</strong> {opportunity.description}</p>
</div>
);
}else{
return null;
}
If you are using some props or state after initial load, it will render multiple times that and at the last render it will have your props or states loaded! So you need to handle you render functions by checking is your state or props ready.

Related

React state not getting updated in Functional Component after fetching from axios and using react-router

So I am trying to update state in the parent component from the child component (child component calls axios) and then I want to send this state from parent component to another child component via route but the state is not getting updated, maybe because I am sending state initailly in the route, can anyone tell me the correct way of sending the updated state in react-route?
Code Snippet:
const ParentComponent = () =>{
const [info, setInfo] = useState([]);
...
...
...
<Switch>
<Route
exact
path="/child1Component"
render={() => <Child1Component setData={setInfo} />}
/>
<Route
exact
path="/child2Component"
render={() => (
<Child2Component dataApp={info} />
)}
/>
</Switch>
}
const Child1Component = (props) =>{
axios
.get("URL", {
})
.then((response) => {
props.setData(response.data);
setLoading(false);
});
...
...
...
}
I want the updated state value to be passed as props in the Child2Component, for now it is not updating initial state.

IonToggle firing twice in React-based Ionic project

In the following block of code, the IonToggle is firing twice for some unknown reason. I had it already replaced with a normal button and it works fine. If I keep the IonToggle and remove the line setUpdating(true) it also works fine.
Is it some known bug, or is there something wrong with this code.
import { AppContext } from './../AppContextProvider';
const LightController: React.FC<InterfaceLamp> = ({ id, color, brightness, turnedOn }) => {
const { state, dispatch } = useContext(AppContext);
const [isUpdating, setUpdating] = useState(false);
const isMount = useIsMount();
const handleUpdateToggle = async (isToggled: boolean) => {
lightService.toggleLight(state.api, id, isToggled, state.auth.username,
state.auth.password).then((res) => {
if (!res.error) {
[...]
dispatch({
key: 'devices',
data: devices,
})
}
setUpdating(false);
})
}
const handleToggle = (isToggled: boolean) => {
setUpdating(true);
handleUpdateToggle(isToggled);
}
return (
<div className="c-light">
<Loader isLoading={isUpdating} message={"Updating devices"} onClose={() => { }} />
<div className="c-light__controls">
<div className="c-light__toggle">
<IonItem lines={"none"}>
<button onClick={(e)=>handleToggle(!turnedOn)}>toggle</button>
<IonToggle checked={turnedOn} onIonChange={(e) => handleToggle(e.detail.checked)}/>
</IonItem>
</div>
</div>
</div>
);
};
export default LightController;
value="true"
why are you setting the value here? dont think it is necessary
As of Ionic React v5.5.0, IonToggle and IonCheckbox have still got the same issue.
The simplest workaround is to add an onClick event listener to the IonItem component that usually wraps the IonToggle component. (Alternately, use any other wrapper component with onClick.) This approach makes it possible to keep the native-looking IonToggle.

REACT Multiple Registration

I have a problem with React, so I created script and it doesn't work.
This should:
Render first state step (it's working) (Component First)
Here is error, it don't see default values.(name & email
After click Save And Continue it should save files to data.
And going to next steps in cases.
The error is
bundle.js:34147 Uncaught ReferenceError: email is not defined
function send(e){
e.preventDefault()
}
function nextStep(){
this.setState({
step:this.state.step + 1
})
}
function nextStep(){
this.setState({
step:this.state.step - 1
})
}
function saveAndContinue(e) {
e.preventDefault()
// Get values via this.refs
var data = {
name : this.refs.name.getDOMNode().value,
email : this.refs.email.getDOMNode().value,
}
this.props.saveValues(data)
this.props.nextStep()
};
var fieldValues = [
name : null,
email : null,
];
function saveValues(fields) {
return (
fieldValues = Object.assign({}, fieldValues, fields)
);
}
class Registration extends React.Component{
constructor () {
super()
this.state = {
step:1
}
}
render() {
switch (this.state.step) {
case 1:
return <First fieldValues={fieldValues}
nextStep={this.nextStep}
previousStep={this.previousStep}
saveValues={this.saveValues} />
case 2:
return <Two fieldValues={fieldValues}
nextStep={this.nextStep}
previousStep={this.previousStep}
saveValues={this.saveValues}/>
case 3:
return <Third fieldValues={fieldValues}
nextStep={this.nextStep}
previousStep={this.previousStep}
saveValues={this.saveValues}/>
case 4:
return <Success fieldValues={fieldValues} />
}
}
}
class First extends React.Component{
render(){
return(
<form onSubmit ={send}>
<div className="group">
<input className="text" type="text" ref="name" defaultValue={this.props.fieldValues.name}/>
<span className="highlight"></span>
<span className="bar"></span>
<label>Write Name</label>
</div>
<div className="group">
<input className="text" type="email" ref="email" defaultValue={this.props.fieldValues.email} />
<span className="highlight"></span>
<span className="bar"></span>
<label>Write Your Mail</label>
</div>
<button onClick={this.saveAndContinue}>Save and Continue</button>
</form>
)
}
}
There is no Two, Third and Success classes in your code, so I'm assuming they are similar to the First class.
A global function doesn't need this keyword. But in this case, you have to put saveAndContinue inside First class if it need to access the state.
In React, normally you don't have to set default value for input.
Link the input value to the state, and then setState in onChange event.
The string in placeholder is shown when the state is empty.
The code below shows how to work with input tag in React:
<input
value={this.state.inputValue}
onChange={e => {
this.setState({ inputValue: e.target.value });
}}
type="text"
placeholder="default value"
/>
Note that the state will updates onChange rather than click the save button.
Does this solve your problem?

rest call with react + redux

I am learning how to use Redux. I would like to create a simple application with only one button. When the button is clicked I want to do a rest api call and when the response comes back the response content needs to be displayed.
What I would like to do is send a store.dispatch(CardAction.GET_CARDS) message to Redux when user clicks on the button. I do not want to call rest api directly from the button's onClick handler.
When the answer is received I intend to to the same: send an event with store.dispatch(CardAction.UPDATE_UI) and somehow at the background I want to update the Redux's status.
I hope that this concept is align with React + Redux.
I have some JavaScript code done but some part of it are missing. Could you please help me to put parts together?
index.jsp
<!DOCTYPE html>
<%#page session="false"%>
<%#page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<html>
<head>
<meta http-equiv="CONTENT-TYPE" content="text/html; charset=UTF-8">
<base href="${pageContext.request.contextPath}/" />
<link rel="icon" type="image/x-icon" href="public/image/favicon.ico">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap-theme.min.css">
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
App.js
let store = createStore(reducers);
ReactDom.render(
<Provider store={store}>
<Card/>
</Provider>,
document.getElementById('root')
);
Card.js
export default class Card extends React.Component {
render() {
return (
<div>
...
<Button onClick={() => store.dispatch(CardAction.GET_CARDS)}>rest call</Button>
</div>
)
}
}
ActionType.js
export const GET_CARDS = 'get-cards';
export const UPDATE_UI = 'update-ui';
CardAction.js
export function getCards(param1, param2) {
return createAction(ActionType.GET_CARDS, (param1, param2) => ({ value1, value2 }))
}
export function updateUi() {
return createAction(ActionType.UPDATE_UI)
}
RootReducer.js
export const reducers = (state = {}, action) => {
return action
};
RestClient.js
export default {
cardPost(param1, param2) {
const url = ...;
fetch(url, {
method: 'POST',
credentials: 'include'
})
.then(response => {
if (response.ok) {
console.info('rest response have arrived');
store.dispatch(CardAction.UPDATE_UI)
} else {
console.info('error appeared during calling rest api');
//store.dispatch(CardAction.SHOW_ERROR)
}
})
.catch(function(err) {
console.info(err + ' Url: ' + url)
})
}
}
You should never call store.dispatch() from a component. Instead, you should import a previously built action and let the Redux flow do the remaining stuff. The reducer shouldn't return an action, instead, it should return a new state, without mutating the previous one. I'd suggest you should first compensate some of the comprehensible lack of experience with Redux, and then you can try to follow along with a React-Redux-Rest tutorial like this one: https://medium.com/#rajaraodv/a-guide-for-building-a-react-redux-crud-app-7fe0b8943d0f#.cnat3gbcx
[EDIT]
Here's what I'd do
// component Card.js
import {getCards} from "CardAction";
export default class Card extends React.Component {
render() {
return (
<div>
...
<Button onClick={getCards(param1, param2)}>rest call</Button>
</div>
)
}
}
// action CardAction.js
const receivedCards = (cards) => ({
type: "RECEIVED_CARDS",
cards
})
export function getCards(param1, param2) {
// idk where you're gonna use these params btw
// also please note that fetch() isn't supported by older browsers. Here I'm showing you a simple example with axios, which basically performs the same operation. Feel free to adapt this example code as you want.
return function(dispatch) {
return axios({
url: server + "endpoint",
timeout: 20000,
method: 'get'
})
.then(function(response) {
let cards = response.data;
dispatch(receivedCards(cards));
})
.catch(function(response){
console.log(response.data.error);
})
}
};
// reducer reducer.js
const initialState = {};
export default (state = initialState, action) => {
switch(action.type) {
case "RECEIVED_CARDS":
return Object.assign({},
state,
{cards: action.cards});
default:
return state;
}
}

react-router > redirect does not work

I have searched on the internet for this topic and I have found many different answer but they just do not work.
I want to make a real redirect with react-router to the '/' path from code. The browserHistory.push('/') code only changes the url in the web browser but the view is not refreshed by browser. I need to hit a refresh manually to see the requested content.
'window.location = 'http://web.example.com:8080/myapp/'' works perfectly but i do not want to hardcode the full uri in my javascript code.
Could you please provide me a working solution?
I use react ^15.1.0 and react-router ^2.4.1.
My full example:
export default class Logout extends React.Component {
handleLogoutClick() {
console.info('Logging off...');
auth.logout(this.doRedirect());
};
doRedirect() {
console.info('redirecting...');
//window.location = 'http://web.example.com:8080/myapp/';
browserHistory.push('/')
}
render() {
return (
<div style={style.text}>
<h3>Are you sure that you want to log off?</h3>
<Button bsStyle="primary" onClick={this.handleLogoutClick.bind(this)}>Yes</Button>
</div>
);
}
}
You can use router.push() instead of using the history. To do so, you can use the context or the withRouter HoC, which is better than using the context directly:
import { withRouter } from 'react-router';
class Logout extends React.Component {
handleLogoutClick() {
console.info('Logging off...');
auth.logout(this.doRedirect());
};
doRedirect() {
this.props.router.push('/') // use the router's push to redirect
}
render() {
return (
<div style={style.text}>
<h3>Are you sure that you want to log off?</h3>
<Button bsStyle="primary" onClick={this.handleLogoutClick.bind(this)}>Yes</Button>
</div>
);
}
}
export default withRouter(Logout); // wrap with the withRouter HoC to inject router to the props, instead of using context
Solution:
AppHistory.js
import { createHashHistory } from 'history';
import { useRouterHistory } from 'react-router';
const appHistory = useRouterHistory(createHashHistory)({
queryKey: false
});
export default appHistory;
Then you can use appHistory from everywhere in your app.
App.js
import appHistory from './AppHistory';
...
ReactDom.render(
<Router history={appHistory} onUpdate={() => window.scrollTo(0, 0)}>
...
</Router>,
document.getElementById('root')
);
Logout.js
import React from 'react';
import appHistory from '../../AppHistory';
import auth from '../auth/Auth';
import Button from "react-bootstrap/lib/Button";
export default class Logout extends React.Component {
handleLogoutClick() {
auth.logout(this.doRedirect());
}
doRedirect() {
appHistory.push('/');
}
render() {
return (
<div style={style.text}>
<h3>Are you sure that you want to log off?</h3>
<Button bsStyle="primary" onClick={this.handleLogoutClick.bind(this)}>Yes</Button>
</div>
);
}
}
this topic helped me a lot:
Programmatically navigate using react router