issue to make a component add a new component - html

I am building a restaurant review website with react js,html and css. I need to make a child component RestaurantInput update a sibling component Restaurant list.
I created handlers which pass informations to App component(the parent) by a callback and when there is an input change in the RestaurantInput it get updated by the handlers. The App component pass then the information to RestaurantList component by props which will render the new restaurant on the UI.
Unfortunatly there is no rendering of the new restaurant . I do not know where i got it wrong. Is there anyone who can help?
I have tried to console log the Restaurants imported from a Json at my local pc. But it look like it was not updated either.
I went to the React js documentation but did not get any clear answer either.
Many solution are for when there is a proper JSON file from the back end and I could not figure out how to apply them in my current situation.
RestauranInput.jsx:
handlechange(e){
const name=e.target.name;
const value=e.target.value;
this.setState((prevState)=>{
prevState.restaurant[name]=value;
return{restaurant:prevState.restaurant};
});
}
handleSave=(e)=>{
this.props.onSave(this.state.restaurant);
this.setState({
restaurant:Object.assign({},Init_value),
error:{}});
e.preventDefault();
}
App.js:
class App extends React.Component {
constructor(props){
super(props);
this.handlerestaurantclick=this.handlerestaurantclick.bind(this);
this.saveRestaurant=this.saveRestaurant.bind(this);
this.state={restaurants:Restaurantlist,showcomponent:false,
restaurantClicked:-1,newrestaurant:{}}
}
saveRestaurant(restaurant){
if(!restaurant.key){
restaurant.key= Object.keys(this.state.restaurants).length;}
this.setState((prevState)=>
{
let restaurants=prevState.restaurants;
restaurants[restaurant.key]=restaurant;
return{restaurants};
});
}
RestaurantList.jsx:
let list=[];
restaurantArray.forEach((item,index)=>{
list.push(<Restaurant key={index} name=
{item.restaurantName}
adress={item.address} ratings={item.ratings} onClick=
{()=>this.handleclick(index)}> </Restaurant>)})
return(<div className="restaurant-list">
<Filter getmin_filter={this.state.handle_min} get_max=
{this.state.handle_max}/>
{list}
</div>);
}

props are not states if they change  on the parent the child components are not rerender so you have to use "componentDidUpdate" check the link below
Re-render React component when prop changes
any communication that is not parent to child, you can either use events or states manager like redux

Related

Angular: Using (click) and routerLink on the same Button

I have a button that redirects to a new page and at the same time should save data to a Service. As I use it now it looks like this:
<button [disabled]="!isValid" (click)="saveToService()" routerLink="/link">Next</button>
Now I wonder if this is best practice. It feels like the html button is somewhat cluttered by so many seperate functionalities. The obvious alternative is to move the router navigation to a function that does both things, as in:
<button [disabled]="!isValid" (click)="saveAndNavigate()">Next</button>
and in ts:
private saveAndNavigate():void { this.service.setData(data); this.router.navigate(['/link]); }
Is there a 'right' way to do this? Are there some unwanted side effects from doing both actions in html?
Thanks
I would suggest you to do it in router promises. So you can:
this.router.navigate(['/link]).then(() => {
this.service.setData(data);
});
I would implement the OnDestroy function in your component, so you can store the data when the component terminates.
Something like this in HTML:
<button [disabled]="!isValid" routerLink="/link">Next</button>
And like this in your component:
import { Component } from '#angular/core';
#Component({...})
export class ThisComponent implements OnDestroy {
ngOnDestroy(){
saveToService()
}
}
If your navigation is performed regardless of the outcome of the service call, then Fatih's answer would work just fine.
On the other hand, and what I've normally seen, is that page navigation should only occur after (successful) completion of the request. If this is the case, I would remove the routerLink directive from your button and keep the (click) function. That function could look like this:
// if your service is making an Http request
public saveToService() {
this.service.saveStuff().pipe(
tap(() => this.router.navigate(['/somewhere']))
)
}
tap simply performs some action without affecting the data stream, so it's perfect for router navigation.

How to change react component on someone else's onclick

I am building a React app where I render a family tree. For that, in each of the family tree component nodes, I have added a onclick which opens a modal (aka popup form) that allows the user to edit the info of that person. In that modal/popup, I have a submit button on the bottom. I want it so that when the submit button is clicked, the input fields in the form (ex: name, parents, etc..) are fetched and updated on the respective node in the tree. I tried this in my code:
submitbtn.onclick = () => {
alert("couple submit clicked!");
info.husband = document.getElementById("hname_inp").value;
info.wife = document.getElementById("wname_inp").value;
modal.style.display = 'none';
alert(info.husband + ' ' + info.wife)
};
return (
<li>
<div onClick={handleClick}>
<span className="male">{info.husband}</span>
<span className="spacer"></span>
<span className="female">{info.wife}</span>
</div>
<Children />
</li>
);
By default, the component shows the info passed through props. When the submit button is clicked, i want the data from the input fields to replace the data in the component. The onclick and the data is feteched fine, but the component is not updated. I am new to React so it might just be a silly mistake, please bare with me.
Finally, and this is a little of the topic, but when I click the submit button, the screen flickers for a second a html page with no formatting shows up then it goes back to normal. What might be the cause for that?
Edit (New Code):
import React from "react";
export default class Couple extends React.Component {
constructor(props) {
super(props);
this.state = {
husband: this.props.husband,
wife: this.props.wife,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const newState = this.state
const modal = document.getElementById('coupleModal');
modal.style.display = 'block';
const submitbtn = document.getElementById('couplesubmitbtn');
submitbtn.onClick = (event) => {
event.preventDefault()
modal.style.display = 'none'
newState.husband = document.getElementById('hname').value;
newState.wife = document.getElementById('wname').value;
}
this.setState(newState);
}
render() {
const children = this.props.children;
return (
<li>
<div onClick={this.handleClick}>
<span className="male">{this.state.husband}</span>
<span className="spacer"></span>
<span className="female">{this.state.wife}</span>
</div>
{children != null && children.length !== 0 ? <ul>{children}</ul> : ""}
</li>
);
}
}
I think you should use different onClick functions on every node.and plus you can change name of the husband using a modal.I have used prompt and saved the data in state for husband and wife
const [Husband, setHusband] = useState("Varun")
const [Wife, setWife] = useState("Alia")
const handleClick = (e) => {
e.preventDefault()
setHusband(prompt("Please enter your Husband Name:"))
};
const handleWife = (e)=>{
e.preventDefault()
setWife(prompt("Please enter your Wife Name:"))
}
return (
<li>
<div>
<span className="male" onClick={handleClick}>{Husband}</span>
<span className="spacer"></span>
<span className="female" onClick={handleWife}>{Wife}</span>
</div>
</li>
);
};
As mentioned in comments before it would be great if you could provide a fiddle etc to look at.
You mentioned that you are new to React so even at the risk of sounding stupid may I just ask are you using some sorf of state handling here? If not then it might be something to look into. If you're already familiar with React state this answer is pointless and should be ignored.
In reactjs.org there are great documentations about what is the difference between state and props?
setState() schedules an update to a component’s state object. When state changes, the component responds by re-rendering.
https://reactjs.org/docs/faq-state.html#what-is-the-difference-between-state-and-props
So in this case information about your family tree would be initialized to state and popup should then update the state via setState. The new input then gets update and UI components rerender.
If I'm right and the state handling will help you go forward I would also recommend to look up React Hooks. Hooks are a new addition in React 16.8 and when you grasp an idea of state using Hooks will be a easy and more elegant way to write your application
==================== Part 2 ====================
Here's the answer to your question you asked below in comments and some additional thoughts:
I assume the flickering is actually page refreshing on submit. So catching the user event and passing it on and calling preventDefault() is a way to go. I will an example below.
Looking at your code I'm more and more convinced that you are indeed lacking the state handling and it's the initial problem here. You could really benefit reading little bit more about it. At the same time it will help you understand better the logic of how React generally works.
Here's another link that might be worth checking out:
https://www.freecodecamp.org/news/get-pro-with-react-setstate-in-10-minutes-d38251d1c781/
And lastly here's the codeSnippet. Note that the wifes input element you're trying to target with getElementById should be document.getElementById("hname") instead of document.getElementById("hname_inp")
submitbtn.onclick = (event) => {
event.preventDefault();
console.log(props.wife);
modal.style.display = "none";
info.husband = document.getElementById("name").value;
info.wife = document.getElementById("hname").value;
alert(info.husband + " " + info.wife);
};
==================== Part 3 ====================
Nice to see that you took a closer look on state handling and have tried it out. I would continue building the knowledge with some additional reading. Here's a good post about Reacts Data handling.
https://towardsdatascience.com/passing-data-between-react-components-parent-children-siblings-a64f89e24ecf
So instead of using state handling separately in different components I would suggest that you move it to App.js as it is the obvious Parent component of others. There you should also think about the data structure. I assume this project is not going to be connected (at least for now) for any api or database and so it's something that would be handled here as well.
So defining some sort of baseline to App.js could look for example like this.
this.state = {
state = { family : [
[{ name: 'kari', gender: male }]
[
{ name: 'jasper', gender: male },
{ name: 'tove', gender: femmale }
],
]
}
}
Then I suggest that you move the handlers here as well. Then writing them here you don't maybe even need separate ones to couples and singles any more.
I'm sorry to hear your still seeing the flickering. My best guess for this is that modal isn't aware about the event.preventDefault. For clarity I would refactor this a bit as well. Generally it's not a good practice to try to modify things via getElements inside React. It's usually all state and props all the way. So I added a few lines of code here as an example of how you could continue on
import React from "react";
import SingleModal from "./Modals/SingleModal";
export default class Couple extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false,
};
this.popUpHandler = this.popUpHandler.bind(this);
}
popUpHandler(event) {
event.preventDefault()
this.setState({visible: !this.state.visible})
}
render(props) {
return (
<>
<SingleModal visible={this.state.visible} popUpHandler={this.popUpHandler }/>
<li>
<div onClick={this.popUpHandler}>
<span className={this.props.gender}>{this.props.name}</span>
</div>
</li>
</>
);
}
}
And similary in SingleModal getting rid of the form submit like this:
<input
type="submit"
value="Submit"
className="submit"
id="singlesubmitbtn"
onClick={(e) => {
e.preventDefault();
props.popUpHandler(e)
}}
/>
PS. I think this is going to be my last answer on this question here. The answer is getting too long and it's starting to drift off topic of the original question. Good luck with your project

Is it possible to copy an object's properties from one component and display them in another component using refs?

Say I have 2 components. One is a table with a list of stores. Each store has properties like color, item, open, closed. The other component is one to create a store.
I want to be able to click on a little copy icon on one of the created stores already, and take that information to the create store component, and populate that component with the properties in order to make changes and create a completely new store.
Is this doable using refs? Or is there a better way of doing this?
Use ref to this task is a mistake. React works using a Virtual DOM that is a cleaner and faster Object Tree with information that will be through to DOM by React DOM the REF API is used to access direct the DOM information, and you don't need any information from DOM to do ur task.
https://reactjs.org/docs/refs-and-the-dom.html
A way yo do what you describe is create a state/setState on the parent component and pass a state for the store component and a setState to the table component for example:
import React, { useState } from 'react'
const StoreComponenet = ({ color, item, open})=>{
// logic of component
return (
<div>
// ...
</div>
)
}
const TableComponent = ({ setStore })=>{
// logic of component
return (
<table>
<tr onClick={() => setStore("blue", {id: 2, name: "BlueStore" }, false)}>
Build blue store
</tr>
...
</table>
)
}
const App = ()=>{
const [store, setStore] = useState(null)
return (
<TableComponent setStore={setStore} />
{
store &&
<StoreComponent
color={store?.color}
item={store?.item}
open={store?.open}
/>
}
)
}

Refresh previous screen on goBack()

I am new to React Native. How can we refresh/reload previous screen when returning to it by calling goBack()?
Lets say we have 3 screens A, B, C:
A -> B -> C
When we run goBack() from screen C it goes back to screen B but with old state/data. How can we refresh it? The constructor doesn't get called 2nd time.
Adding an Api Call in a focus callBack in the screen you're returning to solves the issue.
componentDidMount() {
this.props.fetchData();
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
() => {
this.props.fetchData();
}
);
}
componentWillUnmount() {
this.willFocusSubscription.remove();
}
UPDATE 2023: willFocus event was renamed to focus
componentDidMount() {
this.props.fetchData();
this.focusSubscription = this.props.navigation.addListener(
'focus',
() => {
this.props.fetchData();
}
);
}
componentWillUnmount() {
this.focusSubscription();
}
How about using useIsFocused hook?
https://reactnavigation.org/docs/function-after-focusing-screen/#re-rendering-screen-with-the-useisfocused-hook
const componentB = (props) => {
// check if screen is focused
const isFocused = useIsFocused();
// listen for isFocused, if useFocused changes
// call the function that you use to mount the component.
useEffect(() => {
isFocused && updateSomeFunction()
},[isFocused]);
}
For react-navigation 5.x use
5.x
use
componentDidMount() {
this.loadData();
this.focusListener = this.props.navigation.addListener('focus', () => {
this.loadData();
//Put your Data loading function here instead of my this.loadData()
});
}
For functional component
function Home({ navigation }) {
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
loadData();
//Put your Data loading function here instead of my loadData()
});
return unsubscribe;
}, [navigation]);
return <HomeContent />;
}
On your screen B constructor will work like magic :)
this.props.navigation.addListener(
'didFocus',
payload => {
this.setState({is_updated:true});
}
);
Yes, constructor is called only for the first time and you can't call it twice.
First: But you can separate the data getter/setter from the constructor and put it in a function, this way you can pass the function down to the next Scene and whenever you're going back you may simply recall the function.
Better: You can make a go back function in your first scene which also updates the scene while going back and pass the go back function down. This way the second scene would not be aware of your update function which is reasonable.
Best: You can use redux and dispatch a go-back action in your second scene. Then in your reducer you take care of going back & refreshing your scene.
The built in listener function which comes with React-Navigation would be the easiest solution. Whenever a component is 'focused' on a again by navigating back, the listener will fire off. By writing a loadData function that can be called both when loading the Component AND when the listener is notified, you can easily reload data when navigating back.
componentWillMount(){
this._subscribe = this.props.navigation.addListener('didFocus', () => {
this.LoadData();
//Put your Data loading function here instead of my this.LoadData()
});}
Easy! insert the function inside useFocusEffect(func)
import { useFocusEffect } from '#react-navigation/native'
I have a similar situation and the way i refreshed was to reset the route when the back button is pressed. So, what happens is when the back button is pressed the screen is re-pushed into the stack and the useEffect on my screen loads the data
navigation.reset({
index: 0,
routes: [{ name: "SCREEN WHERE THE GOBACK BUTTON SHOULD GO" }],
});
Update for react-navigation v5 and use the React Hooks. Actually, the use is the same with react base class. For more detail, please checkout the documentation here
Here is the sample code:
function Profile({ navigation }) {
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// do something
});
return unsubscribe;
}, [navigation]);
return <ProfileContent />;
}
As above code, We add the event listener while the variable navigation change then We do something like call function refresh() and finally, we return the function for removing the event listener. Simple!
I think we have a very easy way (which works in 2021) to do so. Instead of using goBack or navigate, you should use push
this.props.navigation.push('your_route_B').
You can also pass params in the same way as we pass in navigate.
The only difference b/w navigate and push is that navigate checks if the route which we are passing exists in the stack. Thus taking us to the older one but, push just sends us there without checking whether that is in the stack or not (i.e, whether the route was visited earlier or not.)
This can be achived by useFocusEffect from '#react-navigation/native'
useFocusEffect will effect every time when screen is focus
Ref: https://reactnavigation.org/docs/use-focus-effect/
import { useFocusEffect } from '#react-navigation/native';
function Profile({ }) {
useFocusEffect(
React.useCallback(() => {
//Below alert will fire every time when profile screen is focused
alert('Hi from profile')
}, [])
);
return // ...code ;
}
You can use this event: navigation.addListener('focus'
And you can implement like this:
const Cards = ({ navigation }) => {
...
useEffect(() => {
const load =async ()=>{
const a = await selectGlobalCards()
}
navigation.addListener('focus',() =>{
load();
});
}, [])
or you can use useIsFocused, and you can use that as a dependecy for useEffect
import { useIsFocused } from '#react-navigation/native'
const Cards = ({ navigation }) => {
const isFocused = useIsFocused()
useEffect(() => {
const load =async ()=>{
const a = await selectGlobalCards()
}
load()
}, [isFocused])
For react navigation (5.x), you just need to add a focus subscription and put your component initializing logic in a separate function like so:
componentDidMount() {
this.init();
this.didFocusSubscription = this.props.navigation.addListener(
'focus',
() => {
this.init();
}
);
}
init = async () => {
//fetch some data and set state here
}
If you're trying to get new data into a previous view, and it isn't working, you may want to revisit the way you're piping data into that view to begin with. Calling goBack shouldn't effect the mounting of a previous component, and likely won't call its constructor again as you've noted.
As a first step, I would ask if you're using a Component, PureComponent, or Functional Component. Based on your constructor comment it sounds like you're extending a Component class.
If you're using a component, the render method is subject to shouldComponentUpdate and the value of your state is in your control.
I would recommend using componentWillReceiveProps to validate the component is receiving the new data, and ensuring its state has been updated to reflect the new data.
If you're using the constructor to call an API or async function of some kind, consider moving that function into a parent component of both the route you're calling goBack from and the component you're wanting to update with the most recent data. Then you can ask your parent component to re-query the API, or update its state from a child component.
If Route C updates the "state/data" of the application, that update should be propagated to a shared parent of routes A, B and C, and then passsed down as a prop.
Alternatively, you can use a state management solution like Redux to maintain that state independent of parent/child components - you would wrap your components in a connect higher-order component to get the latest updates any time the application state changes.
TL;DR Ultimately it sounds like the answer to your question is rooted in where your application state is being stored. It should be stored high enough in your component hierarchy that each route always receives the latest data as a prop, passed from its parent.
Thanks to #Bat.
I have spent a lot of hours on finding the answer and finally, I got a basic solution which is working according to my needs. I was quite worried though.
Simply make a function like this in your previous activity make sure to bind it.
changeData(){
var mydata= salesmanActions.retrieveAllSalesman();
this.setState({dataListFill: mydata});
alert('' + mydata.length);
}
Simple, then in constructor bind this,
this.changeData= this.changeData.bind(this);
After that, as I am using react native navigation, so I will simply pass this function to the second screen just like the code below:
onPress={() => this.props.navigation.navigate('Add Salesman', {doChange:
this.changeData} )}
So when the new screen registered as "Add Salesman" will be called, a parameter named "doChange" which is assigned a function will also be transfered to other screen.
Now, in other screen call this method anywhere, by :
this.props.route.params.doChange();
It works for me. I hope works for you too, THANKS for the idea #Bat.
let we have 2 screen A and B , screen A showing all data . and screen B is responsible for adding that data. we add some data on using screen B and want to show instant changes on Screen A . we use below code in A
componentDidMount(){
this.focusListener = this.props.navigation.addListener('focus', () => {
thi`enter code here`s.startData();
//Put your Data loading function here
});
}
This is what you can do with react navigation v6.
Create a separate stack in stack navigator like this:
const PropertyListStack = () => {
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name={ROUTE_PROPERTY_LIST} component={PropertyList}/>
</Stack.Navigator>
)};
Now, whenever you you want to reload your initial screen navigate using this stack. like this:
navigation.navigate(
ROUTE_DASHBOARD_TABS,
{screen: ROUTE_PROPERTY_LIST_STACK}
);
This will reload your base screen. In my case base screen is PropertyList.
If you know the name of the Screen you want to go , then you can use this code.
navigation.navigate("Screen"); navigation.replace("Screen");
This code works fine if you don't have nested routes.
This answer assumes that the react-native-navigation library is being used, which is unlikely because it doesn't actually have a goBack() method...
The constructor doesn't call a second time because screen A and B are still rendered (but hidden behind screen C). If you need to know when screen B is going to be visible again you can listen to navigation events.
class ScreenB extends Component {
constructor(props) {
super(props);
// Listen to all events for screen B
this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
}
onNavigatorEvent = event => {
switch (event.id) {
case 'willAppear':
// refresh your state...
break;
};
}
Other events: willDisappear, didAppear, didDisappear
An alternate solution to your problem is to use a state management solution like Redux to provide the state to all screens whenever it is updated (rather than just on screen transitions. See old react-native-nav/redux example.

Angular 2 Insert DOM into InfoWindow element that was created by Google Maps V3 api

I am working with the Google Maps V3 inside Angular 2 and I am currently attempting to find a solution for populating InfoWindows with bound links and input values. When an InfoWindow element is created it is a new DOM element and according to Angular 2 docs binding to a DOM element that did not exist on the initialization of the app is not supported. Is there a way to insert or inject bound elements into the newly created DOM, or have a pre-created element containing the bound elements that can be moved into the InfoWindow?
I guess, you should use dynamic components creation in this case. Just try smth like this in your service or component, which knows, when you have to render your content:
import {ComponentResolver, ComponentFactory, ComponentRef, ApplicationRef, Injector} from '#angular/core';
export class SomeServiceOrComponent {
constructor(private resolver:ComponentResolver, private injector:Injector, private appRef:ApplicationRef) {
}
renderComponent() {
return this.resolver
.resolveComponent(YourDynamicComponentClass)
.then((factory:ComponentFactory<YourDynamicComponentClass>) => {
let cmpRef:ComponentRef<YourDynamicComponentClass> =
factory.create(this.injector, null, '.infoWindowsDOMSelector');
(<any>this.appRef)._loadComponent(cmpRef);
return cmpRef;
});
}
}
and just call this method, whether you need it.
(<any>this.appRef)._loadComponent(cmpRef) is a trick from prev versions of RC, but maybe soon it should be resolved