The code in main App component is as follows :
class App extends Component {
componentDidMount(){
console.log(this.ref);
debugger;
}
render() {
return (
<div>
<Header/>
{this.props.children}
<Footer/>
</div>
);
}
}
And one of the components which renders with {this.props.children} is HomePage, where are sections with refs.
The code of a HomePage is as follows :
render(){
return (
<div className="homeMain">
<section ref="info"> <Info/> </section>
<section ref="contact"> <Contact /> </section>
</div>
);
}
How can I get those refs inside App component to be able to pass them as props to header?
I'm trying to do it inside componentDidMount in App component, but console.log(this.refs) is empty.
Any advice?
EDIT
The whole App component :
import React, {Component} from 'react';
import Footer from './common/footer';
import Header from './common/header';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import * as actions from './components/homepage/login/authActions';
class App extends Component {
componentDidMount(){
console.log(this.props.children.refs);
debugger;
}
render() {
return (
<div>
<Header route={this.props.location.pathname}
language={this.props.language.labels}
authenticated={this.props.authenticated}
signoutAction={this.props.actions}
offsets={this.props.offsets}
/>
{React.cloneElement(this.props.children, {
currentLanguage: this.props.language.labels,
authenticated: this.props.authenticated
})}
<div className="clearfix"/>
<Footer currentLanguage={this.props.language.labels}/>
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
language: state.language,
authenticated: state.auth.authenticated,
offsets: state.offsets
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
The React's main idea is passing props downward from parent to children (even to deeper levels of children - grandchildren I mean)
Therefore, when we want the parent to do something which is triggered from (or belongs to) the children, we can create a callback function in the parent, then pass it down to children as props
For your preference, this is a demonstration on how to pass callback functions downward through many levels of children and how to trigger them:
Force React container to refresh data
Re-initializing class on redirect
In your case, you can access refs from children components as follows: (using string for ref - as you stated)
Parent Component:
import React, { Component } from 'react';
// import Child component here
export default class Parent extends Component {
constructor(props){
super(props);
// ...
this.getRefsFromChild = this.getRefsFromChild.bind(this);
}
getRefsFromChild(childRefs) {
// you can get your requested value here, you can either use state/props/ or whatever you like based on your need case by case
this.setState({
myRequestedRefs: childRefs
});
console.log(this.state.myRequestedRefs); // this should have *info*, *contact* as keys
}
render() {
return (
<Child passRefUpward={this.getRefsFromChild} />
)
}
}
Child Component:
import React, { Component } from 'react';
export default class Child extends Component {
constructor(props){
super(props);
// ...
}
componentDidMount() {
// pass the requested ref here
this.props.passRefUpward(this.refs);
}
render() {
return (
<div className="homeMain">
<section ref="info"> <Info/> </section>
<section ref="contact"> <Contact /> </section>
</div>
)
}
}
ref is property of each this.props.children hence you can access ref of child component in parent via ref property on this.props.children
Make sure you access ref after componentDidMount
Edit :
Try below set of code if this works :
var myChild= React.Children.only(this.props.children);
var clone = React.cloneElement(myChild, { ref: "myRef" });
Related
I am trying to display fetched data in child component, using context api. But I'm getting below error on browser
TypeError: render is not a function
The above error occurred in the component:
in AppDataList (at App.js:32)
in div (at App.js:30)
in App (at src/index.js:7)
and below warning
Warning: A context consumer was rendered with multiple children, or a
child that isn't a function. A context consumer expects a single child
that is a function. If you did pass a function, make sure there is no
trailing or leading whitespace around it.
App.js
import React, { Component } from "react";
import "./App.css";
import AppDataList from "./components/AppDataList";
export const AppContext = React.createContext();
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
appData: []
};
}
fetchAppData() {
fetch(` http://localhost:4000/AppDataList`)
.then(res => res.json())
.then(res => {
console.log(res)
this.setState({
appData: res
});
});
}
componentDidMount() {
this.fetchAppData();
}
render() {
return (
<div className="App">
<AppContext.Provider>
<AppDataList />
</AppContext.Provider>
</div>
);
}
}
AppDataList.js
import React, { Component } from "react";
import { AppContext } from "../App";
export default class AppDataList extends Component {
render() {
return (
<AppContext.Consumer>
<div>{context => <p>{context.state}</p>}</div>
</AppContext.Consumer>
);
}
}
I also want to do something like
<AppContext.Provider>
<Child1 />
<Child2 />
<Child3 />
</AppContext.Provider>
and consume data in respective child component.
You have to put the value you want to pass to Consumers via the value prop in the Provider:
<Context.Provider value={{ appData }}>
Below works:
<AppContext.Consumer>
{context => <p>{context.state}</p>}
</AppContext.Consumer>
consumer looks for function, not component.
ref: Seeing "render is not a function" when trying to use Context API
I want to get some props made in the root layer of my react app:
import React from 'react'
import App, { Container } from 'next/app'
export default class MyApp extends App {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
state = {
language: "pl"
};
render () {
const { Component, pageProps } = this.props
return (
<Container>
<Component lang={this.state.language} />
</Container>
)
}
}
so every new React.Component created should inherit those props. But I'm not sure how I can get them. Let's say I have another component which is <Nav/>.
Shouldn't I be able to get it via props.lang inside Nav.
When I try it says lang undefined.
I would suggest moving language to the React Context API
So this way you create a context
// context.js
import React from 'react';
export const LangContext = React.createContext('pl');
and provide it inside _app.js
// app.js
import React from 'react';
import App, { Container } from 'next/app';
import { LangContext } from '../context';
export default class MyApp extends App {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps };
}
state = {
language: 'EN'
};
render() {
const { Component, pageProps } = this.props;
return (
<Container>
<LangContext.Provider value={this.state.language}>
<Component {...pageProps} />
</LangContext.Provider>
</Container>
);
}
}
and whenever you need to access language value you dont need to pass it anymore. It will be available on LangContext. Example usage
// Nav.js
import Link from 'next/link';
import { LangContext } from '../context';
function Nav() {
return (
<LangContext.Consumer>
{lang => {
return (
<div className="site-nav">
<Link href="/">
<a>index</a>
</Link>
<Link href="/about">
<a>about</a>
</Link>
language = {lang}
</div>
);
}}
</LangContext.Consumer>
);
}
export default Nav;
This helps to solve the issue of passing lang props to pages and then to some specific components like Nav. Just wrap a component into a <LangContext.Consumer> if you need it.
Example index.js page
// index.js
import Nav from '../components/Nav';
export default () => (
<div>
<Nav />
<hr />
Welcome to index.js!
</div>
);
** One note: as far as I see you can only use <SomeContext.Provider> inside _app.js
I'm seeing a couple problems in your code example.
First, props are a property on your component, they should be accessed via this.props.
Here is a basic example of passing props to a child component:
import React, { Component } from 'react';
class App extends Component {
render() {
const greeting = 'Welcome to React';
return (
<div>
<Greeting greeting={greeting} />
</div>
);
}
}
class Greeting extends Component {
render() {
return <h1>{this.props.greeting}</h1>;
}
}
export default App;
Using the code sample above, it would seem that your mistake was to use return <h1>{props.greeting}</h1>; instead of return <h1>{this.props.greeting}</h1>;
Second, it would appear that your component setup is a little off. I would expect your component declaration to look something like this:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
In your code sample, there's no constructor function and state doesn't appear to be set as a property of your component.
Inside of the example <Nav/> component, you must specify at least one argument in the component's function if you wish to access this.props. For example:
const Nav = (props) => ( <div> {this.props.lang} </div> )
Hope this helps!
Summary of my comments above:
Did you try props.lang, or, this.props.lang?
Because you need this.props.lang to access the property.
Hrm, just took a quick peek at my own code -- the initial state is set in constructor(props), and is defined like super(); this.state = (somestate);.
Because you need to set the state in the constructor of classes.
I wrote some code to exercise in React. I would like somebody to explain me why if the target of the clickChange is clicked (h3), state does not update.
Below there is my main App component:
import React, { Component } from "react";
import Prova from "./components/prova";
import "./App.css";
class App extends Component {
state = {
name: "giovanni"
};
clickChange = () => {
this.setState({ name: "joe" });
};
render() {
return (
<div>
<h3>SONO APP</h3>
<Prova onClick={this.clickChange} provaProp={this.state.name} />
</div>
);
}
}
export default App;
Below another Component, imported and called (and rendered) into the main App component.
Now, as you can see i set up a method, clickChange, that, once you click on the element, it SHOULD change the state, switching "giovanni" to "joe".
The question is: why it does not trigger? I know that the rendered part of the code it's in the other component, prova, but the state it's in my App component. Therefore, the state is changed internally, without any reference to the external.
import React, { Component } from "react";
class Prova extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<p>{this.props.provaProp}</p>
</div>
);
}
}
export default Prova;
I think you just forgot to trigger the onClick event inside your Prova component.
import React, { Component } from 'react';
class Prova extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div onClick={this.props.onClick}>
<p>{this.props.provaProp}</p>
</div>
)
}
}
export default Prova;
demo
class App extends React.Component {
state = {
name: "giovanni"
};
clickChange = () => {
this.setState({ name: "joe" });
};
render() {
return (
<div>
<h3>SONO APP</h3>
<Prova onClick={this.clickChange} provaProp={this.state.name} />
</div>
);
}
}
class Prova extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div onClick={this.props.onClick}>
<p>{this.props.provaProp}</p>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
For props you will get it in componentWillReceiveProps()
Usage in child component
componentWillReceiveProps(props){
console.log(props.provaProps);
}
So whenever a state of parent component gets updated it updates the props as well but to get updated props in child components we use componentWillReceiveProps().
See more here
Additionally you forgot to attach click event in props
<div onClick={this.props.onClick}>
<p>{this.props.provaProp}</p>
</div>
Because you have not tiggered onClick event in child Component.
change code in Prova component as
<div onClick={this.props.onClick}>
<p>{this.props.provaProp}</p>
</div>
resolved react example here
I want to pass a value to a div with id good in my index.html but it brings this error, Objects are not valid as a React child (found: [object HTMLDivElement]). If you meant to render a collection of children, use an array instead. in TestComponent (at App.js:49)
in div (at App.js:28)
in Apps (at index.js:7)
Please what am I doing wrong
TestComponent.js
import React, { Component } from 'react';
class TestComponent extends Component {
componentDidMount(){
console.log("Great");
}
render() {
// var {test} = this.props;
return (
<p>
{this.props.test}
</p>,
document.getElementById("good")
);
}
}
export default TestComponent;
App.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import TestComponent from "./components/TestComponent"
class Apps extends Component {
constructor(props){
super(props);
}
render() {
return (
<div>
<TestComponent test='doyin'/>
</div>
);
}
}
export default Apps;
Index.html
<div id="good"></div>
A class Component render function shouldn't use document.getElementById, you need to use ReactDOM.render to do that
import React, { Component } from 'react';
class TestComponent extends Component {
componentDidMount(){
console.log("Great");
}
render() {
// var {test} = this.props;
return (
<p>
{this.props.test}
</p>
);
}
}
export default TestComponent;
App
class Apps extends Component {
constructor(props){
super(props);
}
render() {
return (
<div>
<TestComponent test='doyin'/>
</div>
);
}
}
ReactDOM.render(<Apps />, document.getElementById("good"))
export default Apps;
In TestComponent.js, inside render function you are trying to return two elements, <p> and document.getElementById("good"). Probably you just wanted to return <p>:
render() {
return <p>{this.props.test}</p>;
}
Also, it looks like you've mistaken React.Component.render with ReactDOM.render(element, container[, callback]) where the second argument of the functions is the container.
i am currently trying to solve a react problem.
i want to be able to add icons to a container called SalesChannels.
here is the code to the salesChannels page: (JSX)
import React, { Component } from 'react';
import PageContent from 'components/PageContent';
import IBox from 'components/IBox';
export default class SalesChannelsPage extends Component {
constructor(props) {
super(props);
this.state = {
rows: []
}
}
render() {
return (
<PageContent header="Salgskanaler">
<IBox title="Salgskanaler">
</IBox>
</PageContent>
);
}
}
As you can see, i have added a component called IBox. this should be reusable
it looks like this:
import React, {Component} from 'react';
import IBoxTools from './IBoxTools'
export default class IBox extends Component {
render() {
return(
<div className="ibox">
<div className="ibox-title">
{this.props.title}
//this is where the icon should be rendered in the dom
</div>
<div className="ibox-content">
{this.props.children}
</div>
</div>
);
}
}
i have also created another component called IBoxTools - this contains the actual icon / "i" tag:
import React, {Component} from 'react';
export default class IBoxTools extends Component {
render() {
let icon;
if(this.props.icon) {
icon = (
<a title={this.props.title}>
<i onClick={() => this.iconClicked()} className={`pull-right ${this.props.icon}`}></i>
</a>
);
}
return icon;
}
iconClicked() {
console.log('icon clicked')
}
}
So what i am trying to do is add multible icons to the SalesChannels page, inside the IBox tag, without making the IBox component dependent on it.
I hope you can help. Thanks!
You can pass components or arrays of components in props just like children
<IBox
title="Salgskanaler"
icons={[
<IBoxTools key="icon1" icon="foo" />,
<IBoxTools key="icon2" icon="bar" />
]}
>
{/* normal children here */}
</IBox>
Then inside IBox you write { this.props.icons } which will render the icons (or whatever else) if you pass it in.. otherwise will display nothing if the prop is undefined.
You could just use a condition if you don't want to display the icon every time. If I understand what you're attempting to do ...
import React, {Component} from 'react';
import IBoxTools from './IBoxTools'
export default class IBox extends Component {
render() {
return(
<div className="ibox">
<div className="ibox-title">
{this.props.title}
//this is where the icon should be rendered in the dom
{(condition) ? <IBoxTools /> : ""}
</div>
<div className="ibox-content">
{this.props.children}
</div>
</div>
);
}
}
Or if you need to do it multiple times, like you said, then I would use underscore.js or something like it that has a map function.
{_.map(icons, function(icon) {
return (
<IBoxTools />
)
})}
Or some combination of the two.