Is it ok to have own property in react component? - reactjs

class VideoList extends Component {
constructor(props) {
super(props);
this.videoList = props.videos.map((video) => {
return <VideoListItem video={video} />
})
}
render() {
return (
<ul className="collection">
{this.videoList}
</ul>
)
}
}
I'm just wondering if it's allowed to have my own property in react component.

You can have such a property but you need to keep in mind that when you store some value in such a property react will not re render a component - so if you are using that value in render, you might not see the updated value. With setState that's not the case. If you have something in state and then update the state react will re render the component.
There was some guideline on what to put in state (from Dan Abramov), short summary here:
if you can calculate something from props, no need to put that data in state
if you aren't using something in render method, no need to put that in state
in other cases, you can store that data in state

Well, it is ok to have your own property in your react component. No one will blame you.
But don't forget to ship it with propType, it will save you lot time (catch bugs with type checking).
reference: https://facebook.github.io/react/docs/typechecking-with-proptypes.html

I think you're referring to having the videoList stored onto the Component instance?
You could store the list of videos on state, but it seems unecessary to do this and I would simplify VideoList to be a stateless functional component that renders the list of videos passed in as a prop:
const VideoList = ({ videos }) => (
<ul className="collection">
{videos.map(video => <VideoListItem video={video} />)}
</ul>
);
The official docs don't actually explain the syntax above, but it is basically syntactic sugar for a React component with no state, that just accepts props. The ({ videos }) syntax is ES6 Destructuring in action. The VideoList component receives props and this syntax extracts props.videos as a variable on the component.
Additionally, as you're rendering a list of items, you should provide some kind of unique key for each VideoListItem as you render it e.g.
<VideoListItem key={video.id} video={video} />

Related

Using props inside render method as a condition for changing UI?

I have a div which has some class "xyz" at initial state.
Now some change occurs inside my global redux state and I got the props in render method like
render(){
if(this.props.privateMode) { div.class = "abc"; }
return ( <div/> )
}
Is this the right way to update a class for a div when something changes on the global state ( redux ) ?
This is a two part issue.
How to handle the class update.
The className update see the above answer.
How to handle the rerender of the app
Anytime the redux store is update it triggers a rerender of the application. You can grab those updates directly in the render method as the props will be updated. Or in the componentWillReceiveProps method you can get the nextProps and compare them to the current props and decide if you wanna handle it in a certain way.
const { privateMode } = this.props;
<div className={`test ${privateMode ? 'abc' : 'xyz'}`}>
...
</div>
Or use Classnames package
const privateModeClass = className('test', {
'abc': privateMode,
'xyz': !privateMode,
});
<div clasName={privateModeClass} />

Functions and re-renders in React Context

I have two questions about the new React Context-api:
The React docs has the following example Updating context from a nested component. Is there some specific reason the toggleTheme-function is declared in the constructor (and not as a class method)?
import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';
class App extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark,
}));
};
// State also contains the updater function so it will
// be passed down into the context provider
this.state = {
theme: themes.light,
toggleTheme: this.toggleTheme,
};
}
render() {
// The entire state is passed to the provider
return (
<ThemeContext.Provider value={this.state}>
<Content />
</ThemeContext.Provider>
);
}
}
function Content() {
return (
<div>
<ThemeTogglerButton />
</div>
);
}
ReactDOM.render(<App />, document.root);
In lot of the examples the Provider-component has the state of a high-up -component as its value (just like in the above example). This means that every time I want to update the Context-value, I need to update the state of the high-up -component. This means that the high-up -component re-renders as the state is updated, which in turn means that all of its child-components also re-render. But all I wanted was for the Consumer-components listening to that Provider-component to re-render. Now basically the whole app re-renders everytime I update the Context-value...Am I missing something?
toggleTheme is passed as a callback and should be bound to correct this. If it was class prototype method, it would requirethis.toggleTheme = this.toggleTheme.bind(this) in constructor any way. See this related question.
As the documentation states,
All Consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update.
A component that contains Provider (App) should be re-rendered to provide new value, while its descendants shouldn't. In this example its direct children (Content) could be PureComponent to prevent unnecessary re-renders in entire hierarchy. There won't be significant performance improvements for a context that affects entire application like theme context.

Giving React component animation on mount

I am trying to make this component move in when mounted but getting
Cannot read property 'enter' of undefined
Here is a simplified code (I have all the CSS classes ready):
class Example extends React.Component {
state = {
transitionIn: false,
};
componentDidMount = () => {
this.setState({ transitionIn: true })
}
render() {
return (
<CSSTransition
in={this.state.transitionIn}
timeout={1000}
className={'wordTransition'}
>
<div>dksjfnsdkjnfj</div>
</CSSTransition>
);
}
}
https://codesandbox.io/s/rj5046zxoo
I believe that the error you were experiencing is one that you solved in the codesandbox.io link you provided above. I was having this same problem. Instead of naming the prop that takes a class name to be used as the prefix for the various transition states classNames (plural) I was using the more familiar className (singular).
To reiterate: inside the <CSSTransition> component, make sure you are using a classNames prop and not className as you would inside of a react component's html elements.
I feel that the choice on the part of the React Transition Group to use a prop called classNames in their component is confusing and should perhaps be reconsidered.

How to change state of component from anywhere without Redux?

Is this bad practices or not ?
export state change function from component
import it from other file.
call the function to change state?
In this way we can change some component state from anywhere.
For example...
We want to change the Model.js state from anywhere.
Modal.js
import React from 'react';
export let toggleModal;
export default class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
};
toggleModal = this.toggleModal;
}
toggleModal = () => {
this.setState({ open: !this.state.open });
};
render() {
const { open } = this.state;
return <div style={{ color: 'red' }}>{open && 'Hello Modal'}</div>;
}
}
App.js(Some Top Level component)
import React from 'react';
import Modal from './Modal';
export default () => (
<>
...
<Modal />
...
</>
);
Somewhere.js
import React from 'react';
import {toggleModal} from './Modal';
export default () => (
<>
<h1>Hello!</h1>
<button onClick={() => toggleModal()}>open Modal!</button>
</>
);
  
But there is no reference in React Official docs, so is this bad practices ?
What React Docs recommends...
Just passing function props to change parent state from parent to children
Use context
Redux or Mobx
But, these are too complex for me.
Example code here
https://next.plnkr.co/edit/37nutSDTWp8GGv2r?preview
Everything seems pretty much overwhelming and difficult at the beginning. But as we get out hands on them, it's give us more confidence to dig into.
I would recommend to use redux that's how we tackled props drilling problem. You can dispatch a action and connect reducer to corresponding component which upon updating state will re render. This is what I recommend to most of the people to learn the tale of redux with a real life example:
Understanding Redux: The World’s Easiest Guide to Beginning Redux
Apart from this you can take Dan Abramov, author of the library, free redux course on egghead.io:
Getting Started with Redux
The problem you run into, almost immediately like your code example does is this:
It will not work: your toggleModal() method expects a this to refer to an actual component instance. When your onClick() handler fires you invoke toggleModal() as a plain function. The this context will be wrong, and so at best (in your example) you will get an error because you try to invoke something undefined, at worst (in general) you end up invoking the wrong method.
When you think about it, for any non-trivial React component you will have a hard time obtaining a reference to the actual instance that is currently being used: you have to make sure that you are not forgetting to invoke the method on the right component instance and also you have to consider that instances may be created/destroyed 'at will' for whatever reason. For example: what if your component is rendered indirectly as part of some other component's render() method? Multiple layers of indirection like that make it even harder.
Now, you could fix all that by abusing ref with abandon but you will find that now you have to keep track of which ref refers to what particular instance, if you happen to have multiple of the components to consider in one render tree...
Whenever you think one component needs to handle the state of its siblings, the solution is usually to lift the state one level up.
export default class Modal extends React.Component {
render() {
const { isOpen } = this.props;
return <div style={{ color: 'red' }}>{isOpen && 'Hello Modal'}</div>;
}
}
export default class Home {
this.state = {
isOpen: false,
};
toggleModal = () => {
this.setState({ isOpen: !this.state.isOpen });
}
render() {
const { isOpen } = this.state;
return (
<>
<h1>Hello {name}!</h1>
<button onClick={() => this.toggleModal()}>open Modal!</button>
<Modal isOpen={isOpen}/>
<p>Start editing and see your changes reflected here immediately!</p>
</>
)
}
}
This way the Home handle the state and your problem is solved.
This can get annoying if the state needs to be "drilled down" to children, that's a problem than redux or react-context can solve.
Here <Modal /> is the child component. So to call a function in a child component you can simply use Ref.
You can refer this page to get more info about Ref.
You can assign a class variable as a ref to this child and use this class variable as an object to call its function.
I found if in special case, my way is okay.
Special case means something like customAlert component.
It is okay only one instance of customAlert component mounted at a time in App.
To achieve this...
1.Use ref to access and change DOM
2.attach state changing function or component to window and call window.function
3.my case: export state changing function and import it from other file.
And here is how to do with react Context
https://next.plnkr.co/edit/EpLm1Bq3ASiWECoE?preview
I think Redux is overkill if the main thing you are interested in is to make some states-like data available and updatable throughout your App without props drilling.
For that purpose, a much simpler approach (maybe not available at the time the question was posted?) is to use react context: https://frontend.turing.edu/lessons/module-3/advanced-react-hooks.html
"context - an API given to us by React, allowing for the passing of
information to child components without the use of props
[...]
useContext - a react hook, allowing functional components to take
advantage of the context API"

GraphQL HOC messes with ref to children | React

I am using React and Apollo for my project.
I got this in my component MenuModal:
onClick = () => {
console.log(this.child)
this.child.onSubmit(); // do stuff
};
render() {
return (
<Modal.Content scrolling>
<MenuEdit
ref={ref => (this.child = ref)} // Using ref to access it's properties
selectedValues={selectedValues}
match={match}
menu={menu}
/>
My component MenuEdit has a function defined in class:
onSubmit = values => {
console.log('calling inner form submit', values);
if (this.child) {
this.child.submitFromOutside();
}
};
I should be able to call onSubmit from MenuModal right?
But I am currently getting this:
And when I console.log this.child in my onClick function I can see this:
So there's no onSubmit function there. When seeing GraphQL I wondered if it had something to do with me exporting the component with the graphQL HOC.
export default compose(
graphql(UPDATE_MENU, { name: 'updateMenu' }),
withApollo,
withRouter
)(MenuEdit);
And when I changed it just to:
export default MenuEdit;
I can see my function
So I wonder how I write my export so I still can access my function in my child. Thanks.
The HOC wrapps your component into another component. You can see this in the React devtools. You will see that the component renders a component around your wrapped component. Something like this
<Apollo(ChildComponent)>
<ChildComponent />
</Apollo>
Your ref then points to the Apollo(ChildComponen) element instance.
What you are doing here looks like an antipattern to me. In React we usually don't call functions on rendered elements (except sometimes DOM elements). The idea is rather that children call functions of their parents by receiving them as properties. The best thing in your case is to get rid of the reference and move your state and actions up the component chain. Alternatively you can use the new render prop style in react-apollo.
There was a contribution to the Apollo repository to address this issue...
https://github.com/apollographql/react-apollo/pull/410
Wrapping your component export like this withApollo(Component, { withRef: true }) will expose your child methods. Accessible using ref.current.wrappedInstance.

Resources