Access to props of a component from a function - javascript

I'm learning react with mobx for state management and recently came across a scenario wherein i might have to deal with a function which could update the props of a react component.
The function is not part of the said component and that function is being exposed via the bootstrap js entry to let any external module access same and initiate re-rendering of the Component.
I'm unable to do so currently due to me being new to react js, Please suggest if it is feasible to do so in react , appreciate the help.
I've tried a few options however to no avail, i couldn't access the props of this component
PropTypes.instanceOf(SampleComponent).isRequired
Below is a sample component of mine
#observer
class SampleComponent extends React.Component {
constructor(props) {
.......
}
state = {
........
};
componentDidMount() {
..........
}
componentDidCatch(err) {
..........
}
componentWillUnmount() {
.........
}
render() {
.............
}
SampleComponent.propTypes = {
sampleProp: PropTypes.bool,
};
SampleComponent.defaultProps = {
sampleProp: false,
};
function updateSampleProp(value) {
?????
}
export default injectIntl(SampleComponent);
export { updateSampleProp }

Related

Convert functional component to class component in React

I got an app that is working on react using a class component, i found a code of a feature that i would like to add to my code but it's made using a functional component. The code is here https://codesandbox.io/s/framer-motion-animate-in-view-gqcc8 but the relevant part is this.
import { useInView } from "react-intersection-observer";
import { motion, useAnimation } from "framer-motion";
import "./styles.css";
function Box() {
const controls = useAnimation();
const [ref, inView] = useInView();
useEffect(() => {
if (inView) {
controls.start("visible");
}
}, [controls, inView]);
I don't know how to add that controls variable in my class component
class App extends Component {
constructor(props) {
super(props);
this.state = {
curtains: null,
loading: true,
renderNav: false
};
}
Should i add it on my state? i don't understand how to make it works in class component
You can't use hooks inside of a class component. What you can do is to write a little wrapper that exposes the ref and controls in a render prop:
const Controls = ({children}) => {
const controls = useAnimation();
const [ref, inView] = useInView();
useEffect(() => {
if (inView) {
controls.start("visible");
}
}, [controls, inView]);
return children(ref, controls);
};
Then you can use it like this:
class App extends Component {
// ...
render() {
return (
<Controls>
{(ref, controls) => (
<motion.div ref={ref} animate={controls}>
{/* content */}
</motion.div>
)}
</Controls>
);
}
}
Lets say you have
const functionalComponent=()=>{
return <h1>Functional componenet</h1>
}
and you want to change it to class component
use this import at the top:
import React,{Component} from "react";
and change your code to something like this:
Class functionalComponent extends Component{
state={}
render(){
return <h1>functional component</h1>;
}
}
your functional component is now changed to class component.
And to use it in your existing class component , you don't need to change your functional component to class component unless you require local state.
with the introduction of react hooks that's also changed i.e, you don't have to change your functional component to class component if you plan to use hooks.
In your code : useEffect is a hook and you can't use it inside a class component.
I would recommend simply importing the functional component inside your class component and if you have to pass some value , you can pass it as a prop.
And as far as importing your functional component is concerned:
import React,{Component} from "react";
import Box from "./Box.js";
class App extends Component {
constructor(props) {
super(props);
this.state = {
curtains: null,
loading: true,
renderNav: false
};
render(){
return(<Box/>);
}
}
You can also use functional components anywhere like a class component. Btw is also using so no need to worry about the thing that you cannot use state in it.
Use:
<Box props={props}/>

Can not access value of props

I was console logging the this.props from the render function-
And in the console I can see these-
However, when I try to access any one of them, say I wanted to access the store object, then if log like this console.log(this.props.store). Like this-
Then I get this-
I have no clue what so ever I am doing wrong. If anyone knows what I am doing wrong, please let me know. Any help is appreciated!
The whole code-
import * as React from 'react';
import { connect } from 'react-redux';
import mapStateToProps from './utilities/mapStateToProp';
//tslint:disable
class App extends React.Component {
constructor(props: any){
super(props);
}
public render() {
console.log(this.props);
return (
<div className="App">
<h1>React + TypeScript = {}</h1>
</div>
);
}
}
export default connect(mapStateToProps)(App);
Save them in your component state in your constructor and access them from there.
constructo(props){
super(props)
this.state = { store: props.store }
}
And in your render()
render(){
const { store } = this.state //use the store const to access to your value
console.log(store);
}
EDITED
About not using stateless component you should declare an interface in your component in order your component understand what kind of props has the state, review this link for more information Typescript and React
interface IMyComponentProps {
someDefaultValue: string
}
interface IMyComponentState {
store: any //dont know what type is your store.
}
class App extends React.Component<IMyComponentProps, IMyComponentState> {
// ...
}
To test a workaround to check if it works try this React.Component<{},any>

Javascript ES6 (React) How to use a method of an imported class?

Using javascript ES6 (React), I'm not able to call a simple method of an imported class.
What's wrong with this code?
TypeError: WEBPACK_IMPORTED_MODULE_1__Seed.a.test is not a
function
// App.js
import React from 'react';
import Seed from './Seed';
class App extends React.Component {
constructor(props) {
super(props);
console.log('start1');
Seed.test();
}
render() {
return("ei");
}
}
export default App;
and
// Seed.js
import React from 'react';
class Seed extends React.Component {
constructor(props) {
super(props);
console.log('seed1');
}
test() {
console.log('seed test');
}
};
export default Seed;
There are few options, depending on what you're trying to do
1) If this function is unrelated to an instance of a Seed, then make it static.
class Seed extends React.Component {
static test() {
console.log('seed test');
}
// ...etc
}
Then you can call it the way you're already calling it.
2) If it needs to be tied to a specific instance of a seed, you could new one up and then call it. For example:
const mySeed = new Seed();
mySeed.test();
Given that Seed is a react component this is very likely not what you want to do, since you should let react do the instantiating of components and then interact with it through props
3) Use refs to let react give you a reference to the component. I'll assume you're using react 16 or higher and thus have access to React.createRef
constructor(props) {
super(props);
this.seedRef = React.createRef();
}
componentDidMount() {
this.seedRef.current.test();
}
render() {
return <Seed ref={this.seedRef}/>
}
This is better, but its still questionable that you would want to interact with a component this directly.
4) Use props, don't call it directly. Exactly how to do this depends what you're trying to do, but suppose you want to only call the method if some condition is true. Then you could pass a prop in to the Seed, and the seed calls the method itself.
// in App:
render() {
render <Seed shouldDoStuff={true} />
}
// In seed:
constructor(props) {
super(props);
if (props.shouldDoStuff) {
this.test();
}
}
You can do that with declare test as static like this
class Seed extends React.Component {
static test() {
console.log('seed test');
}
constructor(props) {
super(props);
console.log('seed1');
}
};
if you want call test in Seed component use Seed.test()
You cannot access a class' method like that since it's not static.
You would need to have App render with a <Seed /> and get a ref to that component.
// App.js
import React from 'react';
import Seed from './Seed';
class App extends React.Component {
constructor(props) {
super(props);
console.log('start1');
this.seedRef = React.createRef();
}
componentDidMount() {
// seedRef is the Seed instance
this.seedRef.current.test();
}
render() {
return(<Seed ref={this.seedRef} />);
}
}
export default App;

Calling function from another component

I am using React v0.14.8. I tried to call the fetchData function from another component. Here is my code:
export default class TagUtils extends React.Component {
deleteTag = () => {
Tags.deleteTag(this.props.tag).then(function(response){
if(response.message === 'tag successfully deleted')
Sidebar.fetchData();
});
}
// other codes
And:
export default class Sidebar extends React.Component {
fetchData() {
Tags.getTags().done((response) => {
this.setState({tags: response.tags || [], loaded: true});
});
}
//other codes
When I called deleteTag, I got this error in my console:
TypeError: _SidebarJsx2.default.fetchData is not a function
You can't call Sidebar.fetchData because fetchData is not a static member of Sidebar, it is an instance member. This means you need an instance of Sidebar to call fetchData on, for example new Sidebar().fetchData(). Of course, this is not how a React component is supposed to be used, and it would not set state on all other Sidebar instances, so it wouldn't be meaningful.
What you want to do is pass a callback to your TagUtils component:
export default class TagUtils extends React.Component {
deleteTag = () => {
Tags.deleteTag(this.props.tag).then((response) => {
if(response.message === 'tag successfully deleted')
this.props.onDeleteTag();
});
}
}
export default class Sidebar extends React.Component {
fetchData() {
Tags.getTags().done((response) => {
this.setState({tags: response.tags || [], loaded: true});
});
}
render() {
return (
{ this.state.tags.map((tag) =>
<TagUtils tag={tag} onDeleteTag={this.fetchData} />) }
);
}
}
If you have to thread this callback through several layers of components that's okay, that's typical in React. However, if you find yourself passing a lot of stuff down props through many component layers that seem out of place, or trying to reconcile changes across large horizontal spaces in your app, this is a primary use-case for things like Flux and Redux.

React rails app not executing onClick handler when props are used in a conditional expression

I've been having an issue all day that has been driving me nuts. My project is basically building a chatbot with the React Rails gem, with a top level Alt Container, main component and a child component.
I'm having issues getting the onClick handlers in the child component to fire under specific circumstances.
The problem
I can not get the onClick function of the ExampleChildComponent component to fire when running in React Rails and if the component is being rendered inside a conditional statement which uses an Alt js store.
The app works perfectly fine when hosted with Webpack, but using the React-Rails gem, Babelify, and Browserify I get the above issue.
Project description
I'm using the following React related technologies:
Babelify
React
React Rails
Webpack
Alt
I'm developing side by side with Webpack and Rails. I work in Webpack and then switch over to Rails to make sure everything works. My project works 100% perfectly using Webpack, React hot loader and HTML Webpack Plugin but breaks upon being run by Rails.
I have a top level component which wraps everything into an Alt Container.
import React from 'react';
import AltContainer from 'alt/AltContainer';
import ExampleStore from './stores/ExampleStore';
import ExampleActions from './actions/ExampleActions'
import ExampleComponent from './components/ExampleComponent';
export default class ChatContainer extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
ExampleStore.fetchSurvey();
}
render() {
return(
<AltContainer
stores={{ExampleStore: ExampleStore}}
actions={{ExampleActions: ExampleActions}}>
<ExampleComponent/>
</AltContainer>
);
}
}
In my main ExampleComponent I have the following code, which swaps in and out components depending on my Alt store's state:
import React from 'react';
import ExampleChildComponent from './ExampleChildComponent';
export default class ExampleComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
const isReady = this.props.ExampleStore.isReady;
return <div>{isReady ? <ExampleChildComponent/>: 'Loading...'}</div>;
}
}
An example child component type would be the following FaceMood component:
import React from 'react';
export default class ExampleChildComponent extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
console.log('Example child component clicked');
}
render() {
return(
<div onClick={this.handleClick}>
Example Child Component
</div>
);
}
}
This all works perfectly fine in Webpack but in React-Rails the handleClick function is not being executed when I click on the <div> in the ExampleChildComponent.
Interestingly, I managed to narrow the issue down to the following line of code in my ExampleComponent component:
return <div>{isReady ? <ExampleChildComponent/>: 'Loading...'}</div>;
Now if I don't refer to the this.props.ExampleStore.isReady at ALL, the onClick event handler works fine:
return <div><ExampleChildComponent/></div>
This is of course not ideal, as I want my Pane to show different component types depending on the current progress in the chatbot conversation.
I'm pretty much at my wits end so I hope some of you can help me!
Extra information
The prop this.props.ExampleStore.isReady is set using Alt.js' async datasource methods. Its a javascript promise that sets the isReady flag to true after the data fetching has been completed.
Here is the code for the Flux actions:
import alt from '../utils/Alt';
class ExampleActions {
fetchSurvey() {
this.dispatch();
}
updateSurvey(survey) {
this.dispatch(survey);
}
fetchSurveyFailed(errorMessage) {
this.dispatch(errorMessage);
}
}
export default alt.createActions(ExampleActions);
Now the code for the Flux store:
import alt from '../utils/Alt';
import ExampleActions from '../actions/ExampleActions';
import ExampleSource from '../sources/ExampleSource';
class ExampleStore {
constructor() {
this.isReady = false;
this.bindListeners({
handleFetchSurvey: ExampleActions.FETCH_SURVEY,
handleUpdateSurvey: ExampleActions.UPDATE_SURVEY,
handleFetchingSurveyFailed: ExampleActions.FETCH_SURVEY_FAILED
});
this.registerAsync(ExampleSource);
}
handleFetchSurvey() {
}
handleUpdateSurvey() {
this.isReady = true;
}
handleFetchingSurveyFailed(errorMessage) {
}
}
export default alt.createStore(ExampleStore, 'ExampleStore');
Now the code for the flux datasource:
import ExampleActions from '../actions/ExampleActions';
const MockQuestions = [
{
}
];
const ExampleSource = () => {
return {
fetchSurvey() {
return {
remote() {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (true) {
resolve(MockQuestions);
} else {
reject('Fetch failed');
}
}, 500);
});
},
local() {
return null;
},
loading: ExampleActions.fetchSurvey,
success: ExampleActions.updateSurvey,
error: ExampleActions.fetchSurveyFailed
};
}
};
}
export default ExampleSource;
The onClick() function can be seen in the react dev tools app also, its just not executing it!
Screenshot

Categories