Update property in a component from parent-child component - reactjs

In the root of my Application I have a Header component that contains an image. The root also contains a Page component which holds different content based on the Route.
The Header component has a property named HeaderImage (url) and I'd like to be able to update the HeaderImage (url) from the Page component.
Can anyone suggest a good approach, I was thinking of just adding some logic to the Header component that switched the HeaderImage (url) based on the Route but I'd like a little more flexibility. I'd ideally like to be able to update the image from the Page component children.

You can Pass the url to root and let root update it for Header using props like :
class Root extends React.Component {
render() {
constructor(props){
super(props);
this.state={
urlForHeader : null
}
this.updateUrl = this.updateUrl.bind(this);
}
function updateUrl(url){
this.setState({
urlForHeader : url
})
}
return (
<div>
<Header
url={this.state.urlForHeader}
/>
<PageContent
updatingFunction={this.updateUrl}
/>
</div>
);
}
}

Maybe what you need is to implement componentWillUpdate in your component, see this docs about it.
React js ComponentWillUpdate

Related

How to re render a static component in reactjs

I am developing a webapp in reactjs using typescrpit in visual studio 2017, which is very new technology for me. I am stuck at a problem where i dont know how to re-render a component from another component.
I have a log in page. What i want to do is display the username in my header component when the user logs in. Only problem is, the header component is common for all my web pages. Following is my layout.tsx file:-
export interface LayoutProps {
children?: React.ReactNode;
}
export class Layout extends React.Component<LayoutProps, {}> {
public render() {
return <div>
<Header />
<div className='layout_wrapper'>
{this.props.children}
</div>
<Footer />
</div>;
}
}
This is the only file where i have used my header component so that i wont have to use it in every component i create. componentDidMount() of header component will check for access token and make an api call to get user details. Now my question is, how can i re-render this component from another component so that when the user logs in, he can see his name in this header component? Please tell me if i need to provide more code if my question is not clear. Thanks
Considering this is a small app, this solution will work. But it shouldn't be used when the app isn't a small one, because it will make the code complex.
So, according to information given by you, the hierarchy is as follows:
<Header>
<SignIn>
<SignInContent/>
</SignIn>
</Header>
,where SignInContent component is calling the api. We will define a function in Header, and pass it as props to the SignIn component
export class Header extends React.Component<HeaderProps, HeaderState> {
constructor(){
this.state = { isLoggedIn: false }; //Add this to existing state variables
}
render() {
return {
<SignIn setIsLoggedInTrue={this.setIsLoggedInTrue.bind(this)} />
}
}
componentDidUpdate(prevProps, prevState) {
if(this.state.isLoggedIn && !prevState.isLoggedIn) {
// Make the api call for fetching user details
}
}
setIsLoggedInTrue() {
this.setState({isLoggedIn: true});
}
}
And in the SignIn component, again in the render method pass the props to SignInContent like this:
<SignInContent setIsLoggedInTrue={this.props.setIsLoggedInTrue} />
once it is logged in, you can call this.props.setIsLoggedInTrue function from the SignInContent component. This should solve your purpose
Your component will be re-rendered if the supplied props have changed.
By looking at your code, the value of this.props.children should change which will eventually trigger a re-render of your component
Also, if you want to know how to trigger it at login. There should be a componentWillReceiveProps method which checks the login state of the user, and if found logged in will update the values passed to the component :)
EDIT
Here's a sample code to your problem. Since this.props.children is used. You'll need to use it like this.
render() { return (
<Layout>
{this.state.userName}
</Layout>
);}
Now call the api and set the userName

React: should get url in child component times or should get it once in ancestor then pass down as props layer by layer?

I have an ancestor component , and a Name component which is the 4th generation child of Index. There is a new feature I have to add to this app, to make Name s to be red or be bold or add some prefix according to url params, such as "&red=true&bold=true&prefix=true".
I am really tired of passing single prop layer by layer, because I have to add "propA={this.props.propA}" layer by layer, if there is a new param, then add again layer by layer.
Or maybe I should pass all the props, which I don't like, that means pass all props layer by layer.
The third way is getting url params in the Name component constructor itself, then, one done, all done. But, it will get url params in each Name initializion scope, so that will be a lot, I am worring about the performance or the logic of my way is not right.
So, which way is better:
1 passing single props layer by layer, add prop to pass if there is a new one to pass layer by layer.
2 passing all props layer by layer.
3 get url params in the smallest component which will use it.
or is there a another way?
Thank you.
Index.js
class Index extends React.component {
render() {
return <A />
}
}
ReactDOM.render(
<Index/>,
document.getElementById("App")
);
A.js
class A extends React.component {
render() {
return <B />
}
}
B.js
class A extends React.component {
render() {
return <C />
}
}
C.js
class A extends React.component {
render() {
return <Name />
}
}
Name.js
class Name extends React.component {
construtor(props){
super(props);
this.name = null;
this.namePrefix = getUrlParam(window.location.search).nameprefix //boolean
}
render() {
if (this.namePrefix) {
this.name = 'The Genius ' + 'Jack';
}
else {
this.name = 'Jack';
}
return <div>{this.name}<div>
}
}
Using MobX or the new React Context API you can have selected items available to the whole DOM tree and any component that needs some attribute can subscribe to them.
https://reactjs.org/docs/context.html
https://mobx.js.org/getting-started.html
This is the place in your application where you should think about introducing Redux (or Flux). In a nutshell, every component will be able to trigger action that gets data from unique store that holds all the data for your application. Props will flow from root component down the component tree and every component will be able to 'ask' for data anytime triggering action that gets data from backend or extracting it via props.
More about Redux : https://redux.js.org/introduction

Trying to render a new instance in ReactJS

As an example (real tried code)
I have a component of which I want to initiate a NEW instance for rendering.
import React, { Component } from 'react';
export default class TinyObject extends Component {
constructor(props) {
super(props);
console.log("TinyObject constructor");
}
render() {
console.log("TinyObject render");
return (
<div>HEY THIS IS MY TINY OBJECT</div>
);
}
}
Then in main App constructor I do the following:
var myTinyObject = new TinyObject();
var myArray = [];
myArray.push(myTinyObject);
this.state = {testing: myArray};
Then a created a function to render this:
renderTest()
{
const {testing} = this.state;
const result = testing.map((test, i) => {
console.log(test);
return {test};
});
}
And I call this from the App render function like this:
render() {
const { gametables, tableActive } = this.state;
console.log("render");
return <div><div>{this.renderTest()}</div></div>;
}
It runs, no errors.
I see console log of the following:
console.log("TinyObject constructor");
console.log(test);
But I don't see console log of the TinyObject render nor do I see the render output.
Thanks to lustoykov answer I got a little further
JSX: var myTinyObject = <TinyObject />;
works!
but in the real app I add a little more and don't know how to do it here.
return <GameTable key={'gt'+index} data={table} settings={this.settingsData} sendTableNetworkMessage={this.sendTableNetworkMessage} />
this is the way I was rendering; and I needed more instances of GameTable
now the question is; how do I add the arguments like data & settings to myTinyObject.
thanks for helping so far.
You don't manually instantiate react component, use JSX or createElement. For instance
via JSX
var myTinyObject = <TinyObject prop1={prop1} prop2={prop2} />;
via React.createElement
var myTinyObject = React.createElement(TinyObject, { prop1, prop2 }, null);
I would definitely check out some tutorials and how React works at a basic level. You aren't really going to call your react components like you would normally do in javascript since the render function returns jsx.
Fundamentally, React is what is called a single page application. That means that your browser will load up a single html file with a div. Now that div will be where React performs its magic by using Javascript to change stuff around.
It is easiest for me to think of React as a tree. You create these components that you place on the DOM or in your HTML and React will add and remove them downwards. For instance, take a look at this picture of twitter.
So first the Feed component is going to be put on the DOM. Then the Feed component will render the Tweet components. So as you can see the rendering goes in one direction, downwards.
Now, as you can see your render methods are not returning javascript. It is returning something that looks like HTML but we call it JSX. That means we want to render it a little differently with our react classes.
If we have a child component:
class Child extends React.Component {
render() {
return <h1>Hello, I am inside the parent component</h1>;
}
}
We can call the render method like this:
class Parent extends React.Component {
render() {
<Child /> //This is how I use the Child class
}
}
Now the reason why react is so performant is that the child cannot be re-rendered unless we do 1 of two things:
It is a component with a state and we call a method setState()
We pass down new props to a child component from the parent component
You can read about it here
Now the only way to get React to call that render function again is by doing those two things.

Child component changing parent component state

Here there is app root component passing a handleHeaderTitle function to all its children by cloning.
Inside children components, the function is called in componentWillMount()
and as a result root component updates header text based on route.
Root :
{
this.props.children &&
React.cloneElement(this.props.children, {
handleHeaderTitle: this.handleHeaderTitle
})
}
handleHeaderTitle(title) {
this.setState({ headerTitle: title });
}
Child:
componentWillMount() {
this.props.handleHeaderTitle("Profile");
}
Profile.propTypes = { handleHeaderTitle: React.PropTypes.func };
Is this right usecase for adding redux since the state is not really local here as is being set from a child component on its parent ?
I think I will need to create actions like SET_PROFILE_HEADER and inside reducers, combine these. But still figuring out how to inform container root component that child component has changed the header title
To answer your question directly, you don't need Redux to solve your problem. Redux helps you when you have to maintain a lot of state, or when your state needs to be reflected parts of the application that don't have a close common ancestor. In other words, when it gets too cumbersome to use good old fashioned React state.
A more common approach to your specific problem in React would be to include your layout in the child component instead of the root. This avoids the issue of trying to get the child to control content in the parent. It helps preserve the "unidirectional data flow" which is what React is all about.
class Page extends React.Component {
render() {
return (
<div>
<div className='profile-header'>
{ this.props.headerTitle }
</div>
<div className='profile-content'>
{ this.props.children }
</div>
</div>
)
}
}
class MyFirstPage extends React.Component {
render() {
return (
<Page headerTitle='Header Title 1'>
<div>My content here</div>
</Page>
)
}
}

How to pass props to react-router 1.0 components?

Please note, that although the question itself is largely a duplicate of this, it concerns a different version which should support this. The linked question already accepted an answer on an old version
I'm pretty confused about what the intended workflow is.
Let's say I have a menu system where clicking on each item uses react-router to navigate to an area which pulls some data from the server.
url: yoursite/#/lists/countries
----------------------------
Billing Codes | <<Countries>> | Inventory Types
---------------------------------------------------------
Countries:
---------------
Afghanistan
Azerbaijan
Belarus
with routes something like
Route #/lists component: Lists
Route billing-codes component: BillingCodes
Route countries component: Countries
Route inventory-types component: InventoryTypes
I don't want to preload data from the server until an area is navigated to, so in my Countries component's on componentWillMount I fire an event (I'm using reflux but...whatever) that triggers a store to do an ajax request and update itself with the current list of countries.
Now the Countries component reacts to that change in state by updating the countries in its props. Except - reasonably - that generates an invariant error because I shouldn't be updating props on a child component, I should update it at the top level. But the top level is the router itself so now I'm just lost - where am I supposed to listen to changes and update props from?
(Cross-posted to the issue tracker as I think it needs some clearer documentation)
Reading the react-router 0.13 -> 1.0 Upgrade Guide and this example led me to the following:
{ this.props.children &&
React.cloneElement(this.props.children, {newprop: this.state.someobject }) }
Instead of including the child components directly, we clone them and inject the new properties. (The guard handles the case where there is no child component to be rendered.) Now, when the child component renders, it can access the needed property at this.props.newprop.
The easy way is to just use this.state, but if you absolutely have to use this.props then you should probably extend Router.createElement.
First add the createElement prop to your Router render.
React.render(
<Router history={history} children={Routes} createElement={createElement} />,
document.getElementById('app')
);
Wrap all of your components in a new Container component.
function createElement(Component, props) {
return <Container component={Component} routerProps={props} />;
}
Your Container component will probably look something like this. Pass an onLoadData function down to your component.
import React from 'react';
import _ from 'lodash';
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { props: props.routerProps };
}
onLoadData(props) {
var mergedProps = _.merge(this.state.props, props);
this.setState({ props: mergedProps });
}
render() {
var Component = this.props.component;
return <Component {...this.state.props} onLoadData={this.onLoadData.bind(this)} />;
}
}
Then from your loaded component, when you get your data back from the server, just fire this.props.onLoadData(data) and the data will be merged with your current props.
Read the Router.createElement Documentation

Resources