--How to make this React/Redux code DRY - javascript

I have repetitive code that I do not know how to make DRY ( Don't Repeat Yourself ).
Here are two components "talking" via dispatch() and React's auto re-render.
this.map is repeated twice.
This module will dispatch actions on a click.
import React from 'react';
import { connect } from 'react-redux';
class Icon extends React.Component {
constructor(props) {
super(props);
this.map = {
paper: 'bg_paper.jpg',
light_wood: 'bg_wood.jpg',
graph: 'bg_graph.jpg'
};
}
flip () {
this.props.dispatch({type: 'updateIcon', bg_key: $A.nextKey(this.map, this.props.state.bg_key)});
}
render () {
const style = {
// ... snip
}
return (
<img id = 'bar_icon' onClick={this.flip.bind(this)} style={style} src='_images/sv_favicon.svg'/>
)
}
}
const mapStateToProps = state => {
return {
state: state.Icon
}
}
export default connect(mapStateToProps)(Icon);
while this component will auto re-render. It all works fine. I just want to make it DRY.
import React from 'react';
import { connect } from 'react-redux';
// ... snip
class FrameBody extends React.Component {
constructor(props) {
super(props);
this.map = {
paper: 'bg_paper.jpg',
light_wood: 'bg_wood.jpg',
graph: 'bg_graph.jpg'
};
}
render () {
const style = {
backgroundImage: 'url(' + '_images/' + this.map[this.props.state.bg_key] + ')'
};
return (
<div id='contents' style={style}>
</div>
)
}
}
const mapStateToProps = state => {
return {
state: state.Icon
}
}
export default connect(mapStateToProps)(FrameBody);
What can I do so that there are not two instances of this.map?

You can extract the logic of this.map out to a class function.
getBackgroundImageKey = () => {
const backgroundMap = {
paper: 'bg_paper.jpg',
light_wood: 'bg_wood.jpg',
graph: 'bg_graph.jpg'
}
return backgroundMap[this.props.bg_key]
}
Take a step further and add another function to return the URL and add string interpolation.
getBackgroundImageURL(){
const backgroundMap = {
paper: 'bg_paper.jpg',
light_wood: 'bg_wood.jpg',
graph: 'bg_graph.jpg'
}
return `url(_images/${backgroundMap[this.props.bg_key]})`;
}
Which will let you define the style like this
const backgroundImage = this.getBackgroundImageURL()
const style = { backgroundImage };

Well since you're already using Redux and dispatching an action to flip, why don't you move that logic there?
Keep the current image in the store so you can get it in connect, make your flip action creator a thunk that holds that "map" and decides what's the next image.
Instead of DRYness, your code lacks separation of concerns. The switch/Icon UI component would be much more reusable and terse if it only called a prop whenever the user clicks "flips". Connect this onFlip to the action creator I mentioned and you have the logic in one place, and the UI to interact in another.

Related

Chain connect/mapStateToProps/mapDispatchToProps functions for code reuse in react-redux

Say I have two redux connected components. The first is a simple todo loading/display container, with the following functions passed to connect(); mapStateToProps reads the todos from the redux state, and mapDispatchToProps is used to request the state to be provided the latest list of todos from the server:
TodoWidgetContainer.js
import TodoWidgetDisplayComponent from '...'
function mapStateToProps(state) {
return {
todos: todoSelectors.getTodos(state)
};
}
function mapDispatchToProps(dispatch) {
return {
refreshTodos: () => dispatch(todoActions.refreshTodos())
};
}
connect(mapStateToProps, mapDispatchTo)(TodoWidgetDisplayComponent);
The second redux component is intended to be applied to any component on a page so that component can indicate whether a global "loading" icon is displayed. Since this can be used anywhere, I created a helper function that wraps MapDispatchToProps in a closure and generates an ID for each component, which is used to make sure all components that requested the loader indicate that they don't need it anymore, and the global loader can be hidden.
The functions are basically as follows, with mapStateToProps exposing the loader visibility to the components, and mapDispatchToProps allowing them to request the loader to show or hide.
Loadify.js
function mapStateToProps(state) {
return {
openLoader: loaderSelectors.getLoaderState(state)
};
}
function mapDispatchToProps() {
const uniqId = v4();
return function(dispatch) {
return {
showLoader: () => {
dispatch(loaderActions.showLoader(uniqId));
},
hideLoader: () => {
dispatch(loaderActions.hideLoader(uniqId));
}
};
};
}
export default function Loadify(component) {
return connect(mapStateToProps, mapDispatchToProps())(component);
}
So now, if I have a component that I want to give access to the loader, I can just do something like this:
import Loadify from '...'
class DisplayComponent = new React.Component { ... }
export default Loadify(DisplayComponent);
And it should give it a unique ID, allow it to request the loader to show/hide, and as long as there is one component that is requesting it to show, the loader icon will show. So far, this all appears to be working fine.
My question is, if I would like to apply this to the todos component, so that that component can request/receive its todos while also being allowed to request the loader to show while it is processing, could I just do something like:
TodoWidgetContainer.js
import Loadify from '...'
import TodoWidgetDisplayComponent from '...'
function mapStateToProps(state) {
return {
todos: todoSelectors.getTodos(state)
};
}
function mapDispatchToProps(dispatch) {
return {
refreshTodos: () => dispatch(todoActions.refreshTodos())
};
}
const TodoContainer = connect(mapStateToProps, mapDispatchTo)(TodoWidgetDisplayComponent);
export default Loadify(TodoContainer);
And will redux automatically merge the objects together to make them compatible, assuming there are no duplicate keys? Or will it take only the most recent set of mapStateToProps/mapDispatchTo unless I do some sort of manual merging? Or is there a better way to get this kind of re-usability that I'm not seeing? I'd really rather avoid having to create a custom set of containers for every component we need.
connect will automatically merge together the combination of "props passed to the wrapper component", "props from this component's mapState", and "props from this component's mapDispatch". The default implementation of that logic is simply:
export function defaultMergeProps(stateProps, dispatchProps, ownProps) {
return { ...ownProps, ...stateProps, ...dispatchProps }
}
So, if you stack multiple levels of connect around each other , the wrapped component will receive all of those props as long as they don't have the same name. If any of those props do have the same name, then only one of them would show up, based on this logic.
Alright, here is what I would do. Create a higher order component (HOC) that adds a new spinner reference to your reducer. The HOC will initialize and destroy references to the spinner in redux by tying into the life cycle methods. The HOC will provide two properties to the base component. The first is isLoading which is a function that takes a boolean parameter; true is on, false is off. The second property is spinnerState that is a readonly boolean of the current state of the spinner.
I created this example without the action creators or reducers, let me know if you need an example of them.
loadify.jsx
/*---------- Vendor Imports ----------*/
import React from 'react';
import { connect } from 'react-redux';
import v4 from 'uuid/v4';
/*---------- Action Creators ----------*/
import {
initNewSpinner,
unloadSpinner,
toggleSpinnerState,
} from '#/wherever/your/actions/are'
const loadify = (Component) => {
class Loadify extends React.Component {
constructor(props) {
super(props);
this.uniqueId = v4();
props.initNewSpinner(this.uniqueId);;
this.isLoading = this.isLoading.bind(this);
}
componentWillMount() {
this.props.unloadSpinner(this.uniqueId);
}
// true is loading, false is not loading
isLoading(isOnBoolean) {
this.props.toggleSpinner(this.uniqueId, isOnBoolean);
}
render() {
// spinners is an object with the uuid as it's key
// the value to the key is weather or not the spinner is on.
const { spinners } = this.props;
const spinnerState = spinners[this.uniqueId];
return (
<Component isLoading={this.isLoading} spinnerState={spinnerState} />
);
}
}
const mapStateTopProps = state => ({
spinners: state.ui.spinners,
});
const mapDispatchToProps = dispatch => ({
initNewSpinner: uuid => dispatch(initNewSpinner(uuid)),
unloadSpinner: uuid => dispatch(unloadSpinner(uuid)),
toggleSpinner: (uuid, isOn) => dispatch(toggleSpinnerState(uuid, isOn))
})
return connect(mapStateTopProps, mapDispatchToProps)(Loadify);
};
export default loadify;
Use Case Example
import loadify from '#/location/loadify';
import Spinner from '#/location/SpinnerComponent';
class Todo extends Component {
componentWillMount() {
this.props.isLoading(true);
asyncCall.then(response => {
// process response
this.props.isLoading(false);
})
}
render() {
const { spinnerState } = this.props;
return (
<div>
<h1>Spinner Testing Component</h1>
{ spinnerState && <Spinner /> }
</div>
);
}
}
// Use whatever state you need
const mapStateToProps = state => ({
whatever: state.whatever.youneed,
});
// use whatever dispatch you need
const mapDispatchToProps = dispatch => ({
doAthing: () => dispatch(doAthing()),
});
// Export enhanced Todo Component
export default loadify(connect(mapStateToProps, mapDispatchToProps)(Todo));

Pass data from react.js Store to Component in a clean way following flux pattern

following the Flux pattern I'm trying to update my component and pass some values (a string and a boolean in this specific case) via the store.
I could not find any non-hacky way to solve this yet i.e. using global vars in the Store and use a getter function in the Store which is called from the component on ComponentWillMount(), not a nice solution.
Here's a stripped down code example to show what im trying to achieve:
ExampleStore.js
import AppDispatcher from '../appDispatcher.jsx';
var displayimportError = false;
var importedID = '';
import axios from 'axios';
class ExampleStore extends EventEmitter {
constructor() {
super();
}
importId(id) {
let self = this;
// fetch data from BE
axios.get('foo.com').then(function(response) {
if (response.data && response.data.favoriteEntries) {
displayimportError = false;
}
self.emitChange();
}).catch(function(error) {
console.log(error);
displayimportError = true;
importedID = id;
self.emitChange();
// now update component and pass displayimportError and
// importedID.
// best would to component.receiveUpdateFromStore(param); but
// it's giving receiveUpdateFromStore is not function back
});
}
}
var favObj = new ExampleStore();
AppDispatcher.register(function(payload) {
var action = payload.action;
switch (action.actionType) {
case 'UPDATE_ID':
favObj.importId(action.data);
break;
}
return true;
});
export default favObj;
As mentioned in the Comment above the best solution in my eyes so far would be to call a function in the component from the store i.e component.receiveUpdateFromStore(param); and then update the component state within that function but even though they seem to be im/exported correctly to me it is returning receiveUpdateFromStore is undefined.
Any other idea how to solve this is appreciated.
//example component
import React from 'react';
import ReactDom from 'react-dom';
import ExampleStore from '../stores/ExampleStore.jsx';
class ExampleComponent extends React.Component {
constructor(props) {
super(props);
}
receiveUpdateFromStore(param) {
this.setState({'exampleText': param.text, 'exampleBoolean': param.bool});
}
render() {
return <div className="foo">bar</div;
}
}
export default ExampleComponent;
Any idea how to pass data from store to a component and update component state in a nice way?
I would hang your store state on the store class instance itself -- something like this.state.displayimportError = true -- and then have the component subscribe to the store:
import React from 'react';
import ExampleStore from '../stores/ExampleStore.jsx';
class ExampleComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
importError: ExampleStore.state.displayimportError,
};
}
componentWillMount() {
ExampleStore.on( 'change', this.updateState );
}
componentWillUnmount() {
ExampleStore.removeListener( 'change', this.updateState );
}
updateState = () => {
this.setState( state => ({
importError: ExampleStore.state.displayimportError,
})
}
render() {
return <div>{ this.state.importError }</div>
}
}
NOTE: Above code untested, and also using class properties/methods for binding updateState.

setState/use State in external function react

Considering this pseudocode:
component.js
...
import {someFunc} from "./common_functions.js"
export default class MyComp extends Component {
constructor(props) {
super(props);
this.someFunc = someFunc.bind(this);
this.state = {...};
}
_anotherFunc = () = > {
....
this.someFunc();
}
render() {
...
}
}
common_functions.js
export function someFunc() {
if(this.state.whatever) {...}
this.setState{...}
}
How would I bind the function someFunc() to the context of the Component? I use it in various Components, so it makes sense to collect them in one file. Right now, I get the error "Cannot read whatever of undefined". The context of this is unknown...
You can't setState outside of the component because it is component's local state. If you need to update state which is shared, create a store (redux store).
In your case, you can define someFunction at one place and pass it the specific state variable(s) or entire state. After you are done in someFunction, return the modified state and update it back in your component using setState.
export function someFunc(state) {
if(state.whatever) {...}
const newState = { ...state, newValue: whateverValue }
return newState
}
_anotherFunc = () = > {
....
const newState = this.someFunc(this.state);
this.setState({newValue: newState});
}
it's not a React practice and it may cause lot of problems/bugs, but js allows to do it:
Module A:
export function your_external_func(thisObj, name, val) {
thisObj.setSate((prevState) => { // prevState - previous state
// do something with prevState ...
const newState = { // new state object
someData: `This is updated data ${ val }`,
[name]: val,
};
return newState
});
}
Then use it in your react-app module:
import { your_external_func } from '.../your_file_with_functions';
class YourReactComponent extends React.Component {
constructor(props, context) {
super(props, context);
this.state={
someName: '',
someData: '',
};
}
handleChange = (e) => {
const { target } = event;
const { name } = target;
const value = target.type === 'checkbox' ? target.checked : target.value;
your_external_func(this, name, value);
}
render() {
return (<span>
{ this.state.someData }
<br />
<input
name='someName'
value={ this.state.someName }
onChange={ this.handleChange }
/>
</span>);
}
}
It's a stupid example :) just to show you how you can do it
The best would obviously to use some kind of external library that manages this. As others have suggested, Redux and MobX are good for this. Using a high-order component to wrap all your other components is also an option.
However, here's an alternative solution to the ones above:
You could use a standard javascript class (not a React component) and pass in this to the function that you are calling from that class.
It's rather simple. I've created a simple example below where the state is changed from a function of another class; take a look:
class MyApp extends React.Component {
constructor() {
super();
this.state = {number: 1};
}
double = () => {
Global.myFunc(this);
}
render() {
return (
<div>
<p>{this.state.number}</p>
<button onClick={this.double}>Double up!</button>
</div>
);
}
}
class Global {
static myFunc = (t) => {
t.setState({number: t.state.number*2});
}
}
ReactDOM.render(<MyApp />, 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>
There is a functional form of setState that can even be used outside of a component.
This is possible since the signature of setState is:
* #param {object|function} partialState Next partial state or function to
* produce next partial state to be merged with current state.
* #param {?function} callback Called after state is updated.
See Dan's tweet: https://twitter.com/dan_abramov/status/824308413559668744
This all depends on what you are trying to achieve. At first glance I can see 2 options for you. One create a child component and two: use redux as redux offers a singular state between all of your child components.
First option:
export default class parentClass extends Component {
state = {
param1: "hello".
};
render() {
return (
<Child param1={this.state.param1}/>
);
}
}
class Child extends Component {
render() {
console.log(this.props.param1);
return (
<h1>{this.props.param1}</h1>
);
}
}
Now the above child component will have the props.param1 defined from the props passed from it's parent render function.
The above would work but I can see you're trying to establish a 'common' set of functions. Option 2 sort of provides a way of doing that by creating a singular state for your app/project.
If you've haven't used redux before it's pretty simple to use once you've got the hang of it. I'll skip out the setup for now http://redux.js.org/docs/basics/UsageWithReact.html.
Make a reducer like so:
import * as config from './config';//I like to make a config file so it's easier to dispatch my actions etc
//const config.state = {param1: null}
//const config.SOME_FUNC = "test/SOME_FUNC";
export default function reducer(state = config.state, action = {}) {
switch(action.type) {
case config.SOME_FUNC:
return Object.assign({}, state, {
param1: action.param1,
});
break;
default:
return state;
}
}
}
Add that to your reducers for your store.
Wrap all your components in the Provider.
ReactDOM.render(
<Provider store={store} key="provider">
<App>
</Provider>,
element
);
Now you'll be able to use redux connect on all of the child components of the provider!
Like so:
import React, {Component} from 'react';
import {connect} from 'react-redux';
#connect(
state => (state),
dispatch => ({
someFunc: (param1) => dispatch({type: config.SOME_FUNC, param1: param1}),
})
)
export default class Child extends Component {
eventFunction = (event) => {
//if you wanted to update the store with a value from an input
this.props.someFunc(event.target.value);
}
render() {
return (
<h1>{this.props.test.param1}</h1>
);
}
}
When you get used to redux check this out https://github.com/redux-saga/redux-saga. This is your end goal! Sagas are great! If you get stuck let me know!
Parent component example where you define your callback and manage a global state :
export default class Parent extends Component {
constructor() {
super();
this.state = {
applyGlobalCss: false,
};
}
toggleCss() {
this.setState({ applyGlobalCss: !this.state.applyGlobalCss });
}
render() {
return (
<Child css={this.state.applyGlobalCss} onToggle={this.toggleCss} />
);
}
}
and then in child component you can use the props and callback like :
export default class Child extends Component {
render() {
console.log(this.props.css);
return (
<div onClick={this.props.onToggle}>
</div>
);
}
}
Child.propTypes = {
onToggle: PropTypes.func,
css: PropTypes.bool,
};
Well for your example I can see you can do this in a simpler way rather than passing anything.
Since you want to update the value of the state you can just return it from the function itself.
Just make the function you are using in your component async and wait for the function to return a value and set the state to that value.
import React from "react"
class MyApp extends React.Component {
constructor() {
super();
this.state = {number: 1};
}
theOnlyFunction = async() => {
const value = await someFunctionFromFile( // Pass Parameters );
if( value !== false ) // Just for your understanding I am writing this way
{
this.setState({ number: value })
}
}
render() {
return (
<div>
<p>{this.state.number}</p>
<button onClick={this.double}>Double up!</button>
</div>
);
}
}
And in SomeOtherFile.js
function someFunctionFromFile ( // catch params) {
if( //nah don't wanna do anything ) return false;
// and the blahh blahh algorithm
}
you should use react Context
Context lets us pass a value deep into the component tree without explicitly threading it through every component.
here is a use case from react docs : create a context for the current theme (with "light" as the default).
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
resource: https://reactjs.org/docs/context.html

React HOC to handle clicks outside a given Component

I desire to make a Higher Order Component to manage outside clicks. When it is determined that a user has clicked outside a given component a certain passed function should execute. This HOC takes 2 parameters:
BoundaryComponent: the component we are interested in determining if clicks are outside of it
onOutsideClick: a function to execute when a click occurs outside the component
This ClickOutside component looks like:
import React from 'react'
const { Component } = React
import { findDOMNode } from 'react-dom'
export default function ClickOutside (BoundaryComponent, onOutsideClick) {
return class Wrapper extends Component {
constructor (props) {
super(props)
}
componentDidMount() {
document.addEventListener('click', this.handleClick.bind(this), true)
}
componentWillUnmount() {
document.removeEventListener('click', this.handleClick.bind(this), true)
}
render () {
const props = Object.assign({}, this.props, { ref: this.getContainer.bind(this) })
return (
<BoundaryComponent
{...props}
/>
)
}
getContainer (wrapped) {
this.container = findDOMNode(wrapped)
}
handleClick(e) {
if (this.container && !this.container.contains(e.target) && typeof onOutsideClick === 'function') {
onOutsideClick()
}
}
}
}
And I am trying to utilize this component like this:
import React from 'react'
const { ClickOutside } = 'utils/click-outside'
import { updatePicklistActiveIndex } from 'components/store/actions'
import { getPicklistActiveIndex } from 'components/store/selectors'
import PickList from 'components/picklist'
// ...
function mapDispatchToProps (dispatch) {
return {
updatePicklistActiveIndex: (activeIndex) => { dispatch(updatePicklistActiveIndex(activeIndex)) },
}
}
function mapStateToProps (state) {
return {
picklistActiveIndex: getPicklistActiveIndex(state),
}
}
let onOutsideClick = null // This feels like a code smell
class DropdownPickList extends PickList {
constructor(props) {
super(props)
this.state = {
dropdownVisible: false,
}
onOutsideClick = () => {
this.props.updatePicklistActiveIndex(-1)
this.setState({ dropdownVisible: false })
}
}
// ...
render() {
return (
//...jsx to render DropdownPickList
)
}
}
const pickList = ClickOutside(DropdownPickList, () => { onOutsideClick() })
export default connect(mapStateToProps, mapDispatchToProps)(pickList)
The above implementation works as I would expect...
But defining onOutsideClick outside the context of the DropdownPickList Class, and then overwriting this variable reference within the constructor of this Class just feels wrong. But it seems to be the only way to get access to the props and state within the class needed to close the dropdown.
I also tried using a static method in the DropdownPickList class to handle the onOutsideClick, but the problem with this is this Class is passed as an unrendered component, so I do not have access to any of its instance methods in my handleClick of the HOC.
There must be a better way to do this? Any help or alternative implementation ideas/patterns would be greatly appreciated!
Instead of creating a function, just export the wrapper and change as the following :
BoundaryComponent : move to be the children of the wrapper (so it will be actually a wrapper) like so :
<Wrapper>
<BoundaryComponent />
<Wrapper>
While inside wrapper you will render {this.props.children}
onOutsideClick: the onOutsideClick function move to the props of Wrapper :
<Wrapper onOutsideClick={this.onOutsideClick}>
<BoundaryComponent />
<Wrapper>

Is there a React lifecycle method to do something only when component receive props the first time?

I'm new to React so thank you for your patience in advance. Also using Redux.
I have a list of content pulled from the API, I display the text and a hidden text box and on a state change associated that alternates the visibility of the two. Essentially user can click on the text and edit the text, achieved by inverting the boolean and swapping the display. They can then save it and PUT to server etc.
Since my list length varies, I must initialize a number of state.isVisible[n]. equivalent to the number of content being displayed each time. This number must be counted, after the props come in. I am using Redux so the content is retrieved, stored, then given to props. It's done as the following:
constructor(props){
super(props);
this.state = {
isVisibleObj: {}
}
}
componentWillReceiveProps(){
const { isVisibleObj } = this.state
// set visibility of text box
let obj = {}
Object.keys(this.props.questions).forEach(key => obj[key] = false)
this.setState({isVisibleObj: obj})
}
My initial implementation was that in componentWillReceiveProps I do all the setState() to initialize the isVisible properties to a boolean.
The challenge I am having with this implementation is that, if a user open up multiple items for edit, and if she saves one of them, the PUT request on success would send back the edited content, now updating the store and props. This will trigger componentWillReceiveProps and reset all the visibilities, effectively closing all the other edits that are open.
Any suggestion on how to proceed?
I think you should make two components
List (NamesList.react)
import React, {PropTypes} from 'react';
import NameForm from './NameForm.react';
import Faker from 'Faker'
export default class NamesList extends React.Component {
constructor(){
super();
this.addItem = this.addItem.bind(this);
}
addItem(){
var randomName = Faker.name.findName();
this.props.addName(randomName);
}
render() {
let forms = this.props.names.map((name,i) => {
return <NameForm updateName={this.props.updateName} index={i} key={i} name={name} />
});
return (<div>
<div>{forms}</div>
<button onClick={this.addItem}>Add</button>
</div>);
}
}
NamesList.propTypes = {
names: PropTypes.arrayOf(PropTypes.string).isRequired
};
Form (NameForm.react)
import React, {PropTypes} from 'react';
export default class NameForm extends React.Component {
constructor(props) {
super(props);
this.updateName = this.updateName.bind(this);
this.state = {
showTextBox:false
}
}
updateName(){
this.setState({showTextBox:false});
this.props.updateName(this.props.index,this.refs.name.value);
}
render() {
if(this.state.showTextBox){
return (<div>
<input ref="name" defaultValue={this.props.name} />
<button onClick={this.updateName}>Save</button>
</div>);
}
return (<div onClick={() => {this.setState({showTextBox: !this.state.showTextBox})}}>
{this.props.name}
</div>);
}
}
NameForm.propTypes = {
name:PropTypes.string.isRequired
};
Invoke (App.js)
import React, { Component } from 'react';
import NamesList from './NamesList.react';
class App extends Component {
constructor(){
super();
this.addName = this.addName.bind(this);
this.updateName = this.updateName.bind(this);
this.state = {
names:['Praveen','Vartika']
}
}
addName(name){
let names = this.state.names.concat(name);
this.setState({
names: names
});
}
updateName(index,newName){
let names = this.state.names.map((name,i) => {
if(i==index){
return newName
}
return name;
});
this.setState({names:names});
}
render() {
return (
<NamesList names={this.state.names} updateName={this.updateName} addName={this.addName} />
);
}
}
export default App;
Now if your store changes after user saves something. React wont re-render Child component that didn't change

Categories

Resources