I'm trying to render a list of components using map function in render. When I change state, render function is called as expected but my component is not updated.
I've made a sample codesandbox here
https://codesandbox.io/s/nk24mr55om
What am I doing wrong?
I've added some console logs and it look like secret content should be displayed but it is not
Once your item is pushed into the items array, it will not be updated, ever. A solution to this would be to convert the items you push into arrow functions that will get a show parameter :
items.push(show =>
<div>
{show && <div>I'm secret</div>}
My content
<br />
<input
type="button"
onClick={showSecret}
value="Show secret content"
/>
</div>
);
And then call it in the mapping of your render :
return <div key={index}>{item(show)}</div>;
However, with this solution, clicking on one button will cause every item to show their secret element, if this isn't the behavior you expect and want every item to act on its own, I suggest creating a hook for each item. And ahev them store their own show variable in their state.
I have no experience yet with react hooks, but i tried to understand whats going on.
The variable items you refer to is neither a prop nor in the state of your component, i would make the component stateful and initialize items in the state.
Maybe there is a better way with react hooks that someone else can give
Related
Hello fellow programmers,
I need the data from the global state displayed on a page. To specify: the user has to see the data he entered in a form and make sure it is correct. Main goal is getting it on the page first.
What is global state? It is where the data from input fields is collected and is able to be edited. Here is an example how I get the data:
const [enteredFirstName, setEnteredFirstName] = useState('');
In the form it looks like this:
<Card>
<div className="formControl">
<label htmlFor="first_name">Voornaam</label>
<input
type="text"
id="first_name"
value={enteredFirstName}
onChange={event => {
setEnteredFirstName(event.target.value)
}}
/>
</div>
</Card>
Card makes sure that the form data will be collected.
const Card = props => {
return <div className="card">{props.children}</div>
};
Now I need to display the data. I assume that I have to get the props of the children and 'fetch' the data that way? I have tried tutorials but I can't find anything related to my issue.
This picture illustrates how it is supposed to look:
If you'd like to share data between multiple components without having to pass them as props down through all of your components, you can use Context. That will let you write and read from the context in any components that are wrapped within the context provider.
I've used some of your code and created a sandbox to hopefully replicate the functionality you require.
Please check and let me know if this helps: https://codesandbox.io/s/wizardly-visvesvaraya-025cf?file=/src/Form.js
EDIT: To answer your question, you can use the original state and pass it down to another component that only renders based on a condition you've set. - In my example, I am passing the state down to the UserData component which will only render when the data has been submitted.
I'm new to electron, and working on trying to get React/Typescript with Hooks and ApplicationContext to all come together. I didn't create the framework and I need to learn how to make this work. Just setting the context to avoid answers like, use Redux instead. :)
I have a function stateless component that is a form. It needs to be stateless so I have access to values that are held in ApplicationContext. I'm trying to render extra input fields on button click, and so far when I click the button Electron calls the method, and then re-renders. I have searched high and low, and have been banging my head on this for a few hours. I apologize in advance if there is already an answer out there.
So far the code that displays the button looks as such:
<div className="form-group">
<button onClick={() => addUrls()}>
Add a URL
</button>
</div>
And the method is just printing to the console at the moment. It looks as such:
const addUrls = () => {
console.log('clicked')
}
the print statement is getting to the console, and then Electron re-renders. The rest of the component's methods are called, and are behaving in a predictable way. I'm really confused as to why this particular action is causing renders. If anyone can point me in the direction of an answer, or point out where I am doing something dumb in my code, I would be very much grateful.
Try replacing onClick={() => addUrls()} with onClick={addUrls}. The former method will create a new reference on every render, possibly causing rerenders.
Turns out I was doing something dumb. Because my button is inside a form, and I didn't specify the button type, it was doing the default behavior of "submit" which causes a render with the onClick.
The solution was to add type="button" to the button and now it's solved.
<div className="form-group">
<button onClick={addUrls} type="button">
Add a Reference URL
</button>
</div>
I hope this can save someone some time if they come across this problem in the future.
I am having some issues with a particular component not properly updating. This component is effectively modeled after each of the other components, the only difference that I can determine is that it receives props that are array elements; although I have switched the variable being passed so that it renders store elements that are working elsewhere, though I am still getting the same update issue.
The weird thing about this is that the component update does fire, but only when one of the other elements that is properly updating is triggered, so it logs the console object on this instance for two different checks within the componentDidUpdate function.
The overall design is a basic React/Redux app, with a component that is designed to hold/render the audio events. I have a MainLayout component that renders the AudioEngine component, followed by multiple "Panel" components that are only specified for the UI. It is this component that is passed the redux store. It appears that the redusx store is handling state properly, and is passing the data back as expected; however, it is only this element of the UI that is failing to properly trigger an update.
Within the AudioEngine component:
if(this.props.lfoRate != prevProps.lfoRate){
console.log(this.state.lfoRate)
this.setState({
lfoRate: this.props.lfoRate
}, () => {
this.lfo['osc'].frequency.value = this.state.lfoRate
});
}
Here is the return from the MainLayout component, which receives the stor/props (sorry this is still a bit of a work in progress):
return(
<div >
<Header />
<div style={mainLayoutStyle} className={"main-layout"}>
{
this.props.keyOn ? (<AudioEngine lfoRate={this.props.LFObj[2]} gain={this.props.masterGain} freq={this.props.masterFreq} oscArray={this.props.oscArray}
lfoType={this.props.LFObj[1]} lfoFreq={this.props.LFObj[3]} count={count}/>) : (count % 2 == 0 ? (<AudioEngine count={0} gain={0} freq={this.props.masterFreq} oscArray={this.props.oscArray}
lfoType={this.props.LFObj[1]} lfoRate={this.props.LFObj[2]} lfoFreq={this.props.LFObj[3]}/>) : (''))
}
<MainPanel keyToggle={this.props.keyToggle} changeMasterGain={this.props.changeMasterGain}
masterGain={this.props.masterGain} keyOn={this.props.keyOn} baseFrequency={this.props.masterFreq}
changeBaseFrequency={this.props.changeBaseFrequency} />
<div style={{display: 'flex', flexFlow:'column'}} >
<Oscillator addOsc={this.props.addOsc} subOsc={this.props.subOsc} oscArray={this.props.oscArray} />
<LfoPanel lfoRate={this.props.LFObj[2]} lfoFreq={this.props.LFObj[3]} onChange={this.props.changeLFO}
lfoType={this.props.LFObj[1]}/>
</div>
</div>
</div>
)
The LfoPanel component is designed much of the same way as the others...
Any pointers would be quite helpful, perhaps it is passing array elements as properties? If that is the case, that seems like a strange gotcha. Thank-you in advance...
Ok, so after some further research, I realized that it was in fact a mutability issue from Redux. Even though it was technically updating the state, it was not properly immutable. I ended up using the primary solution from this question.
Thanks to those who looked / took time to respond. Hopefully this reference will save another React-Redux a headache later on :)
I have a fixed sidepanel that contains a search bar with filter buttons similar to https://facebook.github.io/react/docs/thinking-in-react.html
The list items are clickable and trigger history.push() for nested path urls.
The nested path structure is
/category-1
/category-1/product-1
/category-1/product-2
/category-2
/category-2/product-1
/category-2/product-2
.
.
.
/category-n/product-m
/category-n/product-k
User can scroll the sidepanel's list and select filters. However, a click on a list item (with onClick() followed by history.push()) causes the whole page (including the sidepanel) to render. This in turn doesn't keep the sidepanel's state so the filters and the scroll position are reset.
The parent render: function() looks like:
return(
<div id="main-view">
<Sidepanel history={this.props.history} properties={this.props.properties} />
React.cloneElement(this.props.children, {this.props});
</div>
);
The React.cloneElement() is the product info container that should render only.
I'm using react-router, react-redux and redux-simple-router.
Do I need to store the sidepanel's list scroll position and filter values outside of the sidepanel? Or is there a better way to keep the sidepanel's state between the url changes?
Your architecture is not aligned with what you want to do.
Simply render the navigation in your top-level, parent component and it will never lose state - for example in your Application component. Then SidePanel wouldn't be affected by route change.
Also you could pass down a callback to set / alter navigation from children, if needed (for example hide() or addMenuItems()).
So, I've been working through my first ReactJS app. Just a simple form where you type in a movie name and it fetches the data from IMDB and adds them as a module on the page. That's all working fine.
However each movie module also had a remove button which should remove that particular module and trigger a re-render. That's not working great as no matter which button you click it always removes the last movie module added rather than the one you're clicking on.
App:
http://lukeharrison.net/react/
Github codebase:
https://github.com/WebDevLuke/React-Movies
I'm just wondering if anybody can spot the reasoning behind this?
Cheers!
Just a hunch, but you should use a unique key, not just the index of the map function. This way React will understand that the movies are identified not by some iterating index, but an actual value, and that will probably solve your issue.
var movies = this.state.movies.map(function(movie, index){
return (
<Movie key={movie} useKey={index} removeMovieFunction={component.removeMovie} search={movie} toggleError={component.toggleError} />
);
});
This is because React re-evaluates your properties, sees that nothing has changed, and just removes the last <Movie /> from the list. Each Movie's componentDidMount function never runs more than once, and the state of Movie 1, Movie 2 and Movie 3 persists. So even if you supply search={movie} it doesn't do anything, because this.props.search is only used in componentDidMount.
I'm not exactly sure why it isn't rendering correctly as the dataset looks fine.
Looking at the code, I would change your remove function to this...
var index = this.state.movies.indexOf(movieToRemove);
console.log(this.state.movies);
if (index > -1) {
this.state.movies.splice(index, 1);
}
console.log(this.state.movies);
this.setState(this.state.movies);
My assumption is that, the state isn't being updated correctly. Whenever updating state, you should always use setState (unless the convention changed and I wasn't aware).
Also, you shouldn't need to explicitly call forceUpdate. Once setState is called, React will automatically do what it needs to and rerender with the new state.
State should be unidirectional (passed top down) from your top level component (known as a container). In this instance, you have state in your top level component for search strings and then you load individual movie data from within the "Movie" component itself via the IMDB API.
You should refactor your code to handle all state at the top level container and only pass the complete movie data to the dumb "Movie" component. all it should care about is rendering what you pass in it's props and not about getting it's own data.