I have a function that is used to change the state of a react component but I'm trying to pass the function in another file. I get the error that the function I'm trying to pass (changeView) is not defined.
This is the App.js
export default class App extends Component {
constructor() {
super();
this.state = {
language: "english",
render: ''
}
}
changeView(view, e){
console.log(view);
this.setState({render: view});
}
_renderSubComp(){
switch(this.state.render){
case 'overview': return <Overview />
case 'reviews': return <Reviews />
}
}
render() {
const {render} = this.state
return <Fragment>
<Header language={this.state.language} />
<Hero />
<Navigation render={render}/>
{this._renderSubComp()}
</Fragment>;
}
}
I'm trying to pass the changeView method to the Navigation.JS component, so I can change the active link as well as render the components listed in the _renderSubComp method above.
import React from "react";
import "./navigation.css";
import { changeView } from "../app";
export default function Navigation() {
return <div className="navigation">
<a onClick={this.changeView.bind(this,
'overview')}>Overview</a>
<a>Reviews</a>
</div>;
}
How should I pass the function to another file so it's able to change the state of my component and render the component I need.
You can't import a method like that. You will pass your function like any other prop to your component and you use there.
I've changed a few things. Firstly, I define changeView function as an arrow one, so we don't need to bind it. Secondly, I pass this function to the component as a prop. Thirdly, I used this function there like:
onClick={() => props.changeView('overview')}
As you can see it is props.changeView not state.changeView
Just go through the official documentation a little bit more. You are confused about states, props and how to pass them to your components.
class App extends React.Component {
constructor() {
super();
this.state = {
language: "english",
render: ''
}
}
changeView = (view, e) => {
console.log(view);
this.setState({ render: view });
}
render() {
const { render } = this.state
return <div>
<Navigation render={render} changeView={this.changeView} />
</div>;
}
}
const Navigation = (props) => {
return <div className="navigation">
<a onClick={() => props.changeView('overview')}>Overview</a>
<a>Reviews</a>
</div>;
}
ReactDOM.render(<App />, document.getElementById("root"));
<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="root"></div>
Related
I'm trying to assign props that I get from parent component and assign it to state in child component(because I want to manipulate the props data I assign it to state first).
When I log the state variable it comes out as an empty array but when I make a new variable in render and assign props to it and log it. It does show the data I need. Also, when I just log this.props I can definitely see that props holds the data I need.
I've assigned props to state a couple of times before, so I'm not sure what is so different this time for it not to work.
Parent component where I pass props to child:
<ShowAvailableTimeslots onClick={this.getSelectedTimeslot} allTimeSlots={this.state.AvailabletimeSlots} />
Child component where I try to assign props to state:
class ShowAvailableTimeslots extends Component {
constructor(props) {
super(props)
this.state = {
sliceEnd: 5,
sliceStart:0,
selectedSlotValue: "",
timeSlotArr: this.props.allTimeSlots,
// timeSlotSlice: timeSlotArr.slice(this.state.sliceStart, this.state.sliceEnd)
}
}
handleTimeSlotClick = (timeSlot) => {
this.setState({ selectedSlotValue: timeSlot }, () => {
this.props.onClick(this.state.selectedSlotValue)
console.log('time slot value', timeSlot)
});
}
previousSlots =()=>{
var test;
}
forwordSlots =()=>{
var test;
}
render() {
var timeSlotArrRender = this.props.allTimeSlots;
return (
<React.Fragment>
{console.log("state", this.state.timeSlotArr)} // --> doesn't show data
{console.log("props", this.props)} // --> does show data
{console.log("render variable", timeSlotArrRender )} // --> does show data
<button className="button btn" onClick={() => this.previousSlots()} disabled={this.state.sliceStart === 0}>left</button>
{/* {this.state.timeSlotArr.map(timeSlot => <a className="timeslot btn " key={timeSlot} value={timeSlot} onClick={() => this.handleTimeSlotClick(timeSlot)}>{timeSlot}</a>)
} */}
<button className="button btn">right</button>
</React.Fragment>
)
}
}
export default ShowAvailableTimeslots
the constructor is called when the component life cycle begins.
You are passing the this.state.AvailabletimeSlots from the parent and by then the constructor have already been called and the assignment for timeSlotArr is already done, so
timeSlotArr: this.props.allTimeSlots // will not work
You have to get help of life cycle methods or hooks
componentWillReceiveProps(nextProps){
this.setState({timeSlotArr: nextProps.allTimeSlots })
}
According to new changes you have to use
static getDerivedStateFromProps(nextProps, prevState){
return {
timeSlotArr: nextProps.allTimeSlots
};
}
I have it working just fine. https://jsfiddle.net/85zc4Lxb/
class App extends React.Component {
render() {
return (<Child passing="I am being passed to child" />);
}
}
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
passedProp: this.props.passing,
}
}
render() {
return (
<React.Fragment>
<button>{this.state.passedProp}</button>
</React.Fragment>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
I try to recreate the scenario and it work try saving all your files again and then check
parents component
import React, { Component } from "react";
import TestOne from "./Components/TestOne/TestOne";
export class App extends Component {
state = {
x: "x data",
y: "y data",
};
render() {
return (
<div>
<TestOne x={this.state.x} allTimeSlots={this.state.y}/>
</div>
);
}
}
export default App;
Child component
import React, { Component } from "react";
export class TestOne extends Component {
constructor(props) {
super(props);
this.state = {
sliceEnd: 5,
sliceStart: 0,
selectedSlotValue: "",
timeSlotArr: this.props.x,
};
}
render() {
var timeSlotArrRender = this.props.allTimeSlots;
return (
<React.Fragment>
{console.log("state", this.state.timeSlotArr)}
{console.log("props", this.props)}
{console.log("render variable", timeSlotArrRender)}
<button className="button btn">right</button>
</React.Fragment>
);
}
}
export default TestOne;
Result:
I think you are missing this.setState({})
i need to know how to fetch state of component from other component by calling the seconed component method inside of first component ?
like :
class General extends Component {
state = {
input:"
}
fetchState() {
return this.state;
}
handleChange () {
this.setState({[e.target.name]: e.traget.value});
}
render() {
return <input type="text" name="input" onChange={this.handleChange.bind(this}>
}
}
class Car extends Component {
render() {
console.log( General.fetchState() );
return null;
}
}
i know i can use static method but i don't have access to this keyword.
The recommended way of doing that kind of things is by composing components and passing the parent's states as props
class General extends Component {
state = { ... }
render () {
return (
<Car {...this.state} />
)
}
}
class Car extends Component {
render () {
console.log(this.props)
return (...)
}
}
Now if you want to share a global state between components could be a good idea to use context api with hooks.
import React, { createContext, useContext } from "react";
import ReactDom from "react-dom";
const initialState = { sharedValue: "Simple is better" };
const StateContext = createContext({});
const General = () => {
const globalState = useContext(StateContext);
return <h1>General: {globalState.sharedValue}</h1>;
};
const Car = () => {
const globalState = useContext(StateContext);
return <h1>Car: {globalState.sharedValue}</h1>;
};
const App = () => {
return (
<StateContext.Provider value={initialState}>
<General />
<Car />
</StateContext.Provider>
);
};
ReactDom.render(
<App />,
document.getElementById("root")
);
Here is the example link.
And here I have a repo with a more elaborated example managing global state with just hooks.
There are many approaches, I suggest using a general state accessible from both components.
Check ReactN for simplicity or Redux for a more robust solution. Note Redux has a big learning curve and quite some boilerplate that, depending on the size of your App, it could not be necessary.
Using globals is not advisable on many situations, but to answer your question, you could also do this:
General component:
class General extends Component {
constructor(){
global.fetchGeneralState = this.fetchState;
}
fetchState = () => {
return this.state;
}
}
Then from the Car component, you can just call: global.fetchGeneralState(); and you will get the state from the General component.
In your current code, the only way to do it is to use new General.
console.log(new General().fetchState());
If you expect to use Car component as a parent of General component, then you can simply use ref. Here is the modified code of yours that I have tested :
import React from "react";
class General extends React.Component {
constructor () {
super();
this.state = {input: ""}
this.handleChange = this.handleChange.bind(this);
}
fetchState() {
return this.state;
}
handleChange(e) {
this.setState({[e.target.name]: e.target.value});
}
render() {
return <input type="text" name="input" onChange={this.handleChange} />
}
}
export default class Car extends React.Component {
constructor () {
super();
this.refToGeneral = React.createRef()
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
console.log(this.refToGeneral.current.fetchState())
}
render() {
return (
<>
<General ref={this.refToGeneral} />
<button type="button" onClick={this.handleClick}>Show State</button>
</>
)
}
}
I have a global variable called global.language. In my CustomHeader component, I have a Button that toggles the language global variable. What I want is to update all my screen components to reflect the language change.
I don't know if the best way to go is to get a reference to the Screens or to use an event library or if there are React friendly ways of doing this.
My CustomHeader.js looks like this:
export default class CustomHeader extends React.Component {
constructor(props) {
super(props);
this.toggleLanguage = this.toggleLanguage.bind(this);
}
render() {
return (
<Button onPress={ this.toggleLanguage } title="Language" accessibilityLabel="Toggle language" />
);
}
toggleLanguage() {
if (global.language == "PT") global.language = "EN";
else if (global.language == "EN") global.language = "PT";
}
}
My Screen.js renders numerous components called Event. This is what my Event.js looks like:
export default class Event extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Card>
<Text>{Event.getTitle(this.props.data)}</Text>
</Card>
);
}
static getTitle(data) {
if (global.language === "PT") return data.title;
else if (global.language === "EN") return data.title_english;
}
}
Live sandbox
In details.
React.createContext we can just export to reuse. But this would be just "generic" context. Better encapsulate data and methods we need into custom container element and HOC:
import React from "react";
const context = React.createContext();
export class I18N extends React.Component {
state = {
language: "en"
};
setLanguage = language => {
this.setState({ language });
};
render() {
return (
<context.Provider
value={{ language: this.state.language, setLanguage: this.setLanguage }}
>
{this.props.children}
</context.Provider>
);
}
}
export function withI18n(Component) {
return props => (
<context.Consumer>
{i18nContext => <Component {...props} i18n={i18nContext} />}
</context.Consumer>
);
}
<I18N> is provider that will typically go just once on the topmost level.
And with HOC withI18n we are going to wrap every element that need access to our language value or ability to update this value.
import React from "react";
import ReactDOM from "react-dom";
import { I18N, withI18n } from "./i18n";
const Header = withI18n(function({i18n}) {
const setLang = ({ target: { value } }) => i18n.setLanguage(value);
return (
<div>
<input type="radio" value="en" checked={i18n.language === "en"} onChange={setLang} /> English
<input type="radio" value="fr" checked={i18n.language === "fr"} onChange={setLang} /> French
<input type="radio" value="es" checked={i18n.language === "es"} onChange={setLang} /> Spanish
</div>
);
});
const Body = withI18n(function(props) {
return <div>Current language is {props.i18n.language}</div>;
});
const rootElement = document.getElementById("root");
ReactDOM.render(<I18N>
<Header />
<Body />
</I18N>, rootElement);
And finally good article with some additional details: https://itnext.io/combining-hocs-with-the-new-reacts-context-api-9d3617dccf0b
say my HOC is:
import React, { Component } from "react";
let validateURL = WrappedComponent =>
class extends Component{
render() {
if( wrappedcomponentnameis === 'xyz')
return ...
elseif(wrappedcomponentnameis === 'abc')
return ...
and so on....
}
};
export default validateURL;
how do I get the name of wrapped component inside this HOC?
You can access it via WrappedComponent.name:
const HOC = WrappedComponent => class Wrapper extends React.Component{
render() {
if (WrappedComponent.name === 'Hello') {
return <WrappedComponent name='World' />
}
return <WrappedComponent/>
}
}
class Hello extends React.Component {
render() {
return <div>Hello {this.props.name}</div>
}
}
const App = HOC(Hello)
ReactDOM.render(
<App />,
document.getElementById('container')
);
<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="container">
<!-- This element's contents will be replaced with your component. -->
</div>
However, I will prefer to pass optional props to the HOC, in order to control its behavior, because it's much safer, rather than relying on WrappedComponent.name.
For example: there are many libraries (as redux, react-router, and etc) which provide some functionality to your components through HOC mechanism. When this libraries wraps your component, then WrappedComponent.name will point to the library HOC name and will break your logic silently.
Here's how you can pass custom props:
const HOC = (WrappedComponent, props) => class Wrapper extends React.Component{
render() {
const { shouldPassName } = props
if (shouldPassName) {
return <WrappedComponent name='World' />
}
return <WrappedComponent/>
}
}
const App = HOC(Hello, { shouldPassName: true })
Ok so I have two files Test.js and Test2.js
Test.js:
import React from 'react';
const hello = ['hello', 'hi', 'sup'];
export const helloWorld = hello.map(helloCode => {
return (
<button onClick={this.handleClick}>{helloCode}</button>
);
});
Test2.js:
import React from 'react';
import { helloWorld } from './Test';
export class RealTest extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('clicked');
}
render() {
return (
<div>
{helloWorld}
</div>
);
}
};
I can't figure out how to get helloWorld to access the onClick function, I have tried to create a props, I have tried binding it, but I cannot get it to work unless it is in Test2.js, but I need it to be in it's own seperate file. Thanks for the help.
#Adam suggesting passing the context down, but I think it's more React like to pass props.
export const HelloWorld = props => hello.map(helloCode => {
return (
<button
key={helloCode} // <--- make sure to add a unique key
onClick={props.handleClick}
>
{helloCode}
</button>
);
});
Then render:
<div>
<HelloWorld handleClick={this.handleClick} />
</div>
The array of JSX accessed via the variable helloWorld does not have any knowledge of what you want the context (e.g. this) to be when it's in it's own file (and, thus, this.handleClick can't be used).
The simplest way is to make it a function so you can pass the correct context:
import React from 'react';
const hello = ['hello', 'hi', 'sup'];
export const helloWorld = (context) => hello.map(helloCode => {
return (
<button onClick={context.handleClick}>{helloCode}</button>
);
});
and then in your render method, pass in the context:
render() {
return (
<div>
{helloWorld(this)}
</div>
);
}
const hello = ['hello', 'hi', 'sup'];
const HelloWorld = (props) => <div>{hello.map(name =>(<button onClick={() => props.handleClick(name)}>{name}</button>))}</div>
class RealTest extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(name) {
console.log('clicked for name ', name);
}
render() {
return (
<div>
<HelloWorld handleClick={this.handleClick} />
</div>
);
}
};
ReactDOM.render(
<RealTest/>,
document.getElementById('root')
);
<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="root"></div>