React instance variable undefined - reactjs

I have a base component where i want to have an instance level variable that is updated by subclasses. The only way i can get this work is to add it to state which i don't want to do due to the fact that the value of this variable is NOT related to the UI so i don't want to trigger a re-render.
So take following component:
class BaseComponent extends React.Component {
constructor(props) {
super(props);
this.params = [];
}
addParam(name) {
this.params.push(name);
}
getUrl() {//this.params is always undefined
return format("{0}?", this.constructor.name, this.params.join("&");
}
}
So this.params is always undefined which makes sense since it is not part of state. This variable is related to the url and not the UI so when the value is updated i don't want to have to trigger a re-render by updating the state.
Not sure what the best approach here is?
Options i know of:
1) Add to state which will trigger re-render
2) Call forceUpdate() which will also trigger re-render
I don't like either of these options so i am hoping someone knows the correct way to do what i am trying to do.

Related

How should I handle component state following single responsibility pattern

I'm new to ReactJs and trying to follow best practices. From my research, I've come across a couple of contradicting articles discussing how implementation should be.
Should state rely on the properties being passed down from a parent component? In the comparisons below, they are both following SRP, but not sure which is best. Would like your advice, Thanks!
1. -- Best Practices for Component State in React.js
First, and probably the most important of all, the state of a component should not depend on the props passed in. (see below for example of what we should not do)
class UserWidget extends React.Component {
// ...
// BAD: set this.state.fullName with values received through props
constructor (props) {
this.state = {
fullName: `${props.firstName} ${props.lastName}`
};
}
// ...
}
2. -- 7 architectural attributes of a reliable React component
Let's refactor to have one responsibility: render form fields and attach event handlers. It shouldn't know how to use storage directly.....The component receives the stored input value from a prop initialValue, and saves the input value using a prop function saveValue(newValue). These props are provided by withPersistence() HOC using props proxy technique.
class PersistentForm extends Component {
constructor(props) {
super(props);
this.state = { inputValue: props.initialValue };
}
// ...
}
3. -- In my case, I have something like the following (wondering if this is an acceptable implementation?) - Should state be handled in Tasks, or in another TasksWithPersistence type of component that sits between TasksWithData and Tasks?
export default function TasksWithData(TasksComponent) {
return class withData extends React.Component {
render() {
const tasks = TaskAPI.getTasks();
return (
<TasksComponent
tasks={tasks}
{...this.props}
/>
)
}
}
}
export default class Tasks extends React.Component {
state = {
tasks: [],
addItemInput: null
};
// ...
componentDidMount() {
this.updateComponentState({tasks: this.props.tasks});
}
componentDidUpdate() {
this.prepUIForNextAddition();
}
// ...
}
The gist of your question seems to revolve around the anti-pattern that is to take some props and duplicate it into the state. This, mutating of props, isn't the purpose of the state. Props are immutable, duping them to the state defeats this design.
The purpose of the state is to manage things that are specific to the React Component, i.e. tightly scoped to only that React component. For instance a showHide switch for something to display within the React component. Think of the state as a locally scoped variable if it helps.
Most of the time this anti-pattern of duping the props can be satisfied by a function within the React object. For example, your state.full_name variable becomes a named function, fullName, bound to the React Component. (all code examples are assuming JSX syntax)
Note: in JavaScript camelcase is the naming structure for functions and variables, I'm assuming you're coming from ruby based on the underscore naming convention. IMO it's best to stick to the convention of the language with which you're writing the code. This is why I use camelcased naming.
...
fullName() {
return this.props.firstName + " " + this.props.lastName
}
...
That function can then be called within the render of the component
# in render() portion of your React component, assuming jsx syntax
<p>Hello, {this.fullName()}</p>
Note: Remember that in ES6 you have to bind the methods in your react class in the constructor or use => syntax so that you can call them with this.
...
constructor(props) {
super(props);
this.fullName = this.fullName.bind(this);
}
...
You could also decompose the relevant parts to a new Component called FullName if it will be utilized by multiple components.
<FullName firstName={this.props.firstName} lastName={this.props.lastName} />
Technically, "the react way" is, at least in this author's opinion, to decompose this into another component for reusability. However component reuse needs to be weighed against the complexity added, i.e. don't optimize prematurely. So you may not want to take that too far at first. The times when it's necessary will emerge naturally.
A very broad generalization of React's props is that they are guaranteed, are immutable, and they flow down like a waterfall from the topmost component. If you need to update them, update them at the highest level where it makes sense.
In a soley React based approach, if you have something that a parent needs to be aware of, "lift" that part of the code up to the parent and vice versa bind it down to the child as a props, e.g. an AJAX function that calls an API. I think of it as trying to keep the components as dumb as possible.
The parent becomes the "source of truth" for the item you "lifted". The parent handles the updates, and then passes the results to the children. So in the parent, it may exist as a state variable and then get passed as props to the child object, which then passes it along as props to it's child object, etc. The children would update as the state gets changed in their parent when it propagates down through the chain as props.
If your app is React only, i.e. no stores that manage objects such as in the flux pattern or redux pattern, you may have to store things in the topmost objet's state which technically could be viewed as bad. As your system becomes more complex, this functionality would be better handled by flux or redux's parts.
Hope this helps!
There is a huge difference between example 1 & 2.
In example #1, the reason it's bad to set state from the those props in that way is that if the props change, the widget will not update. Best practices or not, that is just wrong and bad in any framework. In that particular case, there really is no point in even using the state. Props alone will suffice.
In example #2 the prop is only being used to give the state an initial value (The prop is even named initialValue), implying that further changes to the state will be controlled by the component regardless of prop changes. It does not break single responsibility principle to use props for an initial state, especially when it's explicitly use for that purpose.
I really don't see those two examples as being contradictory because they are completely different. Further, there is no rule in single responsibility principle that you can't set state from props, you just need to pay attention to the context in which you are doing it.

Setting initial state from props

I am trying to set an initial state for a component, from a prop that I have passed through. The prop is passed from App.js where it is populated from an API call.
When I check for a value for the component state there is none. How could this be?
Here is code setting initial state from ListBooks.js
class ListBooks extends Component {
constructor(props) {
super(props);
this.state = {
x: props.books
};
console.log("Check if state has been set")
console.log(this.state.x)
}
Here is where prop is populated in App.js
class BooksApp extends React.Component {
state = {
books: []
}
componentDidMount() {
console.log("Fetching All Books from API")
BooksAPI.getAll().then((books) => {
this.setState({books})
})
}
My overall goal is to be able to update the local state in the ListBooks component - so it updates immediately (haven't finished the code for this part) and then to update the server in background via API. To save a long reload after each update.
You shouldn't do this for sure.
First of all read this tutorial in the docs. It will show you the right way to organize your state.
If you can get values which you need from props - you shouldn't keep it in state - just use props in render.
My overall goal is to be able to update the local state in the ListBooks component - so it updates immediately (haven't finished the code for this part) and then to update the server in background via API. To save a long reload after each update.
This is not a good idea because you need to be sure that data is changed on the server sucessfully otherwise you will display not reliable data which can be confused for user.
For example: Let's imagine you have a list of books which you got from a server. Then user add new book to this list and you update state immediately and then send request to the API and something went wrong there (you have sent invalid data, for example). After that user decided to change the info of this newly added book but when he/she will try to do it - server will, probably, return 404 because there is no such book. It's kinda confusing, isn't it? Does it makes sense? I hope so.
Good luck and enjoy your coding :)
console.log(this.state.x) is undefined since the following code:
componentDidMount() {
console.log("Fetching All Books from API")
BooksAPI.getAll().then((books) => {
this.setState({books})
})
}
Includes two asynchronous calls that are not guaranteed to complete before it is rendered: BooksAPI.getAll() and setState.
But as the second comment to your question indicated, it is strange that you set the state in ListBooks from the passed props. The point in passing props is not to need to set local state in the component that gets the props.
If, as you wrote in the 4th comment on the question, you modify it inside ListBooks, then you probably need to call BooksAPI inside ListBooks and not pass props.

React Re-Render and update issue

Suppose I have 10k of record and whenever I am trying to update state using setState every time component is re-render but I want after all changes on state component should render once only.
example :-
constructor(){
this.state={
employeeList:[{name:a},{name:b},{name:c},{name:d},{name:e}.....]
}
}
I want to update a,b,c,d,e to 1 ,2 ,3 ,4,5 by using this.setState() but after update all value i need to re-render component not after update one by one.
Please help me.
There are two possible ways to do this:
Use the 'shouldComponentUpdate' and decide in it whether to render.
Use a pure component.
Using 'shouldComponentUpdate'
The following text is copied from "How does React decide to re-render a component?":
"By default, shouldComponentUpdate returns true. That’s what causes a component to render everytime there is a state change. However, you can overwrite shouldComponentUpdate to give it more “smarts” if you need the performance boost. Instead of letting React re-render all the time, you can tell React when you don’t want to trigger a re-render."
Use a pure component
The following text is copied from "Why and How to Use PureComponent in React.js": "PureComponent changes the life-cycle method shouldComponentUpdate and adds some logic to automatically check whether a re-render is required for the component. This allows a PureComponent to call method render only if it detects changes in state or props."
You need to handle your state updates differently
Instead of updating state separately for each value, you would update it once like
update() {
const newValues = [1,2,3,4,5];
const newEmployeeList = this.state.employeeList.map((obj, index) => {
return {...obj, name: newValues[index]};
})
this.setState({employeeList: newEmployeeList})
}

Setting default context in React

Is it possible to define default contexts for components, so that if the parent component does not pass down the context to the child, the child does not break? The only thing I can think of at the moment is
constructor() {
super();
if (!this.context.role) {
this.context.role = 'public';
}
}
Would that work?
Context is similar to props except that a change in context doesn't actually trigger a render. Usually context takes its value from a state or a store so that's usually not a problem. Another downside is unlike props, React doesn't provide a way to set a default value for it.
You can probably make a feature request in react's repo. A workaround would be to assign a default value to it before you use it. e.g:
class MyComponent {
// use this.getRole to access the role
getRole() {
return this.context.role || 'public';
}
}
Edit: your code snippet would not work because the context value could possibly get overriden by the parent during the render pipeline; plus it's not documented what would happen when you update context object directly.

How to have different react components share and acknowledge each other's states?

I've frequently encountering dynamic components not being able to update each other directly. Multiple models that update each other's attributes but not their views.
model2View.onClick=() {
model1.change;
model1View.forceUpdate();
}
i've often resorted to throwing in callback functions into the mix which is just a wrapper that updates the model and forceupdate the overhead view after change.
it gets worse when the model update is a modelside function that updates more than one model, in which case I've been using the nearest ancester view to perform the forceupdate.
model2View.onClick=() {
the following 3 could be wrapped in one method on model1
model1.change;
model2.change;
model3.change;
modelsWrapperView.forceUpdate();
}
After many force updates, I am just wondering if that's the way react was meant to be used or I'm missing something.
Here's an oversimplified example of the problem i'm trying to solve:
https://jsfiddle.net/x3azn/ann6qb30/1/
You are attempting to mutate an object that is part of your state and React won't re-render because it doesn't know that something has changed. That's why you're having to call forceUpdate.
You should treat state as immutable and use setState to update it (updated demo):
class TransformersApp extends React.Component {
toggleDeception() {
// Create a new object rather than mutating the existing one.
var newTransformer = Object.assign({}, this.state.transformer, {
isDecepticon: !this.state.transformer.isDecepticon
});
// Update the state.
this.setState({
transformer: newTransformer
});
}
}
This is made clear by the docs for setState:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
You may find it helpful to use something like Immutable.Map as your state object as that will prevent you from mutating the state at all.

Resources