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

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} />

Related

React class component inside react hook - DOM not updating

I have below composition:
const HookComponent = (props)=> {
let [month, changeMonth] = useState(moment());
return (
<ChildComponent
month={month}
onChange={m => changeMonth(m)}
minStep={5}
prevMonthIcon="ion-ios-arrow-left"
nextMonthIcon="ion-ios-arrow-right"
/>
)
}
The ChildComponent is a class component which updates the month using setState. Problem with above is that the change is not reflecting on DOM but the state in parent component is changing (via user input - button which changes the state in ChildComponent). I logged it and confirm the month in parent is changing. Is this some limitation of react when using class components within hooks?
When I convert HookComponent to class component and change month using setState, it works as expected and DOM changes on input change.
It seems that the InputMoment component does not use a month prop, but a moment one.
Also, it seems that InputMoment is returning the same moment instance that is passed as moment prop. This causes that when you execute the changeMonth statement, as the reference does not change, the element is not re-rendered.
You can solve this by storing an object in the state. When you call changeMonth you pass a new object, and the InputMoment is then re-rendered correctly:
const HookComponent = (props)=> {
let [month, changeMonth] = useState({ m: moment() });
return (
<ChildComponent
moment={month.m}
onChange={m => changeMonth({ m })}
minStep={5}
prevMonthIcon="ion-ios-arrow-left"
nextMonthIcon="ion-ios-arrow-right"
/>
)
}

How to get state data from child to parent component?

I have two components, TabTble and MultiSelect.
I'm rendering MultiSelect component in TabTble, so parent component is TabTble and child component is MultiSelect.
I have some state in my child (MultiSelect) component, how to get it in TabTble component.
const selectedData = this.state.multi; this is state data
const {selectedData } = this.props in this way i passed it as props
<MultiSelect selectedData ={ selectedData } /> And render it in child component like this.
The correct way of sharing state variables between components is through redux store. The major advantage is if you have more than just 2 components, then u can use store state in all of them and modify it by dispatching actions which again will make the modified state available to all the components.
you can do like this suppose you have these two components :
class TabTble extends Component {
handleChildData = (fromMultiSelectData) => {
//Do whatever you want to do from this child data
}
return (
<MultiSelect selectedData ={ selectedData } handleChildData = {this.handleChildData}/>
);
}
wherease in MultiSelect component:
class MultiSelect extends Components {
state = {
localData:[] //local state data which we want to send to parent components
}
handleClick = () => {
let {localData} = this.state;
this.props.handleChildData(localStateData)
}
render(){
let {selectedData} = this.props;
return(
somechild
//Note you need some event handler like onChange,onClick to pass this data up
<div onClick={this.handleClick}>Send Data to parent</div> //you can pass any other data from here
)
}
}
// Note this design pattern is not good while designing react code. Its always recommended when you have such prop drill down or use good state management either context api or redux.
I think you should change a bit your code. Put the states who goes to change in Parent and pass it as a props to child, in child you can change the state.
If u are not using Redux I think it is the right way.
See that example:

How to force dom re render in react

I am trying to force a child component to re-render. I have tried this.forceUpdate();, but it does not work. I put console.log statements in my <PostList /> component, and none of them are ever called--not componentDidMount, nor componentWillMount, componentWillReceiveProps, none of them. It's as if the <PostList /> component is never initialized. I am sure it is though, because I know for a fact items.count retrieves my items. Here is my render method:
render() {
const items = this.state.posts;
const postList = items.count > 0 ? (<PostList comingFromSearch={true} xyz={items} />) : (<div></div>)
const navBar = <NavigationBar />
return (
<div><br/>{navBar}
<div className="container">
<h3>Search Results for {this.state.searchTerm}</h3>
<div className="row">
<div className="col-x-12">{postList}</div>
</div>
</div>
</div>
)
}
And here is my api call:
retrieveSearch(term) {
Helpers.searchWithTerm(term).then((terms) => {
const postsWithTermsInTitle = terms.titleResults
this.setState({posts: postsWithTermsInTitle})
this.forceUpdate();
}).catch((error) => {
console.log("error searching: " + error);
})
}
I should note, on my previous page, i had another ` component, and maybe react is using that one instead of this one? I want to force it to use this instance.
If this.forceUpdate(); does not make the whole DOM re-render, how can I do that?
thanks
your PostList and NavigationBar Components might not update because they only update when their props are changed (shallow compare).
PostList might not update when changing the inner content of the array, because the component will shallow compare the new state with the previous one. Shallow comparing an array will basically checked against its length property. which does not change in this case.
Quick Solution
Sometimes you need to update a List, without changing any of its props or the length of the list. To achieve this, just pass a prop to the component and keep incrementing it instead of calling force update.
retrieveSearch(term) {
Helpers.searchWithTerm(term).then((terms) => {
const postsWithTermsInTitle = terms.titleResults
this.setState((curState) => ({posts: postsWithTermsInTitle, refreshCycle: curState.refreshCycle+1}))
this.forceUpdate();
}).catch((error) => {
console.log("error searching: " + error);
})
}
render() {
...
<PostList
...
refreshCycle={this.state.refreshCycle}
/>
...
}
Right solution
The right solution is to provide an itemRenderer which you is a function that returns the an individual item from the list. This function is passed as a prop to the component.
This way you have control over how the items inside the list will appear, also changes inside the itemRenderer function will cause a component update.
itemRenderer(itemIndex) {
return <div>{this.props.item[itemIndex]}</div>;
}
render() {
...
<PostList
itemRenderer={this.itemRenderer.bind(this)}
itemsLength={items.length}
/>
...
}
The itemRenderer will be called inside the PostList in a loop (of length itemsLength). each loop will be passed the index of the current iteration, so you can know which item of the list to return from the function.
This way you can also make your list more scalable and more accommodating.
You can check an implementation of such solution on a list package like this one: https://www.npmjs.com/package/react-list
You can force a re-render of a component and all its children by changing the state of the component. In the constructor add a state object:
constructor(props) {
super(props)
this.state = {
someComponentState: 'someValue'
}
}
Now whenever you do:
this.setState(someComponentState, 'newValue')
It will re-render the component and all its children.
This of course assumes your component is a class based component, not a functional component. However, if your component is a functional component you can easily transform it to a class based component as follows:
class ComponentName {
constructor() {
// constructor code
}
render() {
// render code
}
}
export default ComponentName
Understand that componenet level state is not the same as redux state but is exposed only inside the component itself.

Is it ok to have own property in react component?

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} />

Toggle children in a React stateless component

I was using a stateful component but upon making it stateless I am unclear how/if I can toggle inner fields:
Here's a contrived example. I am actually working with react-redux-form and need to toggle certain input fields based on some radios.
const Sample = (props) => {
let show = true
const toggleMethod = (model, value) => {
show = !show
}
return (
<div>
<Control changeAction={toggleMethod} />
{ show === 'cc' ? <CCFIelds /> : null }
</div>
)
}
The toggle method works great but it will not rerender. Am I obliged to use state for this or is there a way to do it as is?
When #Jason Xu say you can dispatch to change state, it's a way to influence parent and cause rerender, but you don't have to redux.
The easiest way is to let parent (smart container) own and control the show variable, and then pass to your stateless component as a prop. Define the toggleMethod() in parent, and pass down also.
To summarize:
show toggled in parent
prop changes
stateless component rerenders

Resources