I am facing given warning in console:
Warning: A string ref, "grid", has been found within a
strict mode tree. String refs are a source of potential bugs and
should be avoided. We recommend using useRef() or createRef() instead.
Here's my line of codes:
render() {
return (
<div className="notes-app">
<h2 className="app-header">NotesApp</h2>
<NoteSearch onSearch={text => this.handleSearch(text)} />
<NoteEditor onNoteAdd={this.handleNoteAdd} />
<NotesGrid
notes={this.state.filteredNotes}
onNoteDelete={this.handleNoteDelete}
/>
</div>
);
}
The snippet of the code editor shows the lines where the warning is pointed
The NotesGrid component is as follows:
import Masonry from "masonry-layout";
import React from 'react';
import Note from "./Note";
export default class NotesGrid extends React.Component {
componentDidMount() {
var grid = this.refs.grid;
this.msnry = new Masonry(grid, {
itemSelector: ".note",
columnWidth: 200,
gutter: 10,
isFitWidth: true
});
}
componentDidUpdate(prevProps) {
if (this.props.notes.length !== prevProps.notes.length) {
this.msnry.reloadItems();
this.msnry.layout();
}
}
render() {
var onNoteDelete = this.props.onNoteDelete;
return (
<div className="notes-grid" ref="grid"> //here I Have used the ref
{this.props.notes.map(function(note) {
return (
<Note
key={note.id}
onDelete={onNoteDelete.bind(null, note)}
color={note.color}
>
{note.text}
</Note>
);
})}
</div>
);
}
}
What's the best alternative to resolve it?
Rewrite the NotesGrid as below
import Masonry from "masonry-layout";
import React from 'react';
import Note from "./Note";
export default class NotesGrid extends React.Component {
constructor(props) {
super(props);
this.gridRef = React.createRef();
}
componentDidMount() {
this.msnry = new Masonry(this.gridRef.current, {
itemSelector: ".note",
columnWidth: 200,
gutter: 10,
isFitWidth: true
});
}
componentDidUpdate(prevProps) {
if (this.props.notes.length !== prevProps.notes.length) {
this.msnry.reloadItems();
this.msnry.layout();
}
}
render() {
var onNoteDelete = this.props.onNoteDelete;
return (
<div className="notes-grid" ref={this.gridRef}>
{this.props.notes.map((note) => (
<Note
key={note.id}
onDelete={onNoteDelete.bind(null, note)}
color={note.color}
>
{note.text}
</Note>
))}
</div>
);
}
}
The issue is with the component NotesGrid. Check whether the component has usage of 'ref' or not.
If ref is using in that component. create the ref using React.createRef() (if its a class component) or using useRef(if its a functional component)
Related
App.js
import React from 'react';
import './App.css'
import Tools from './components/class/Tools'
import Loading from './components/inc/Loading'
export default class App extends React.Component {
componentDidMount() {
Tools.showLoading(); // or new Tools();
}
render() {
return (
<div className="App">
<Loading />
</div>
)
}
}
Loading.js:
import React from 'react'
export default class Loading extends React.Component {
constructor(props) {
super(props)
this.state = {
display: 'none'
}
}
render() {
return (
<div className="loading" style={{display: this.state.display}}>
<span></span>
</div>
)
}
}
Tools.js
export default class Tools extends React.Component {
static showLoading(){ // or non-static
Loading.setState ...
}
}
I want change display state from outside of Loading component.
I use Loading in whole my project and I want create function for handle it.
Example for another use:
function xxx(){
Tools.showLoading(); // or new Tools();
}
Or:
<span onClick={Tools.showLoading(); // or new Tools();}></span>
Actually, I want create only one function to manage and handle display of Loading.
In Tools.js
let loadingStateSetter = null
export function setLoadingStateSetter(setter) {
loadingStateSetter = setter
return () => loadingStateSetter = null
}
export function setLoadingState(value) {
if (loadingStateSetter !== null) loadingStateSetter(value)
}
In Loading.js:
import { setLoadingStateSetter } from './Tools.js'
export default class Loading extends React.Component {
constructor(props) {
super(props)
this.state = {
display: 'none'
}
}
render() {
return (
<div className="loading" style={{display: this.state.display}}>
<span></span>
</div>
)
}
componentDidMount() {
this.removeStateSetter = setLoadStateSetter((value) => {
this.setState((state) => ({
...state,
display: value,
})
})
}
componentWillUnmount() {
this.removeStateSetter()
}
}
Usage:
import { setLoadingState } from './Tools.js'
function xxx(){
setLoadingState('some value')
}
While you can easily expose a setState function externally, it acts just like any other function, its not usually a good idea. You should instead consider rewriting your Loading component to use the property object to tell it if its loading and track the loading state higher up the component tree where it is accessible by things that would want to change its status.
I think you can using redux as store manager global state
https://redux.js.org/
another way pass it through props and handle it at parent component
I have two commponents. Map and LfMap.
import React, { Component } from 'react';
import { LfMap } from '../../components/Localisation/LfMap';
export class Map extends Component {
constructor(props) {
super(props);
this.state = {
dev: [],
};
}
componentDidMount() {
fetch("URL")
.then(resp => resp.json())
.then(pas => this.setState({dev: pas}));
}
render() {
return(
<React.Fragment>
<LfMap list={this.state.dev}/>
</React.Fragment>
);
}
}
import React from 'react';
import { Map, CircleMarker, TileLayer, Polyline} from "react-leaflet";
import "leaflet/dist/leaflet.css";
import Control from 'react-leaflet-control';
class LfMap extends React.Component {
constructor(props) {
super(props);
this.state = {
devices: this.props.list,
}
}
render() {
return(
<React.Fragment>
<Map
style={{ height: "480px", width: "100%" }}
zoom={12}
center={[22.22, 21.00]}
>
<TileLayer url="http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
</Map>
</React.Fragment>
);
}
}
export { LfMap };
And I'm passing a prop list to LfMap with list of my objects.
But when I'm trying to console.log(this.state.devices) it shows Undefined but it should console my list of objets which i fetched in other component. Why it's happening?
Why set state = props ? is an anti-pattern, you should keep using this.prop.list in the other component.
Do this:
console.log(this.props.list)
it will print [], and then when the results come it will print the full array.
When you need to use this array always use this.props.list.
Based in your comment here is how you give solution to that:
At the parent you add a function
listUpdate(newList){
this.setState({
list: newList
})
}
and then you pass this function to the child
<LfMap list={this.state.dev} listUpdate={this.listUpdate}/>
when you need to update it you need to call this.props.listUpdate.
Always update the props :) that's where the reference is, this is the pattern you should follow.
I have some problem to pass the ref to child element in JSX.
Please, see the following:
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
render() {
return (
<div id="parent" ref={element => (this.parentRef = element)}>
<canvas id="child" width={this.parentRef.offsetWidth} />
</div>
);
}
}
ReactDOM.render(document.getElementById("app"), <App />);
I want to access #parent width from #child. How it is possible?
This is very late, but I have a solution using the latest React hooks and functional components for future viewers. Due to the way how refs work, they do not cause a re-render on value change. However, mozilla added something called ResizeObserver: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver , which watches components for resize.
import React, { useRef, useEffect, useState } from "react";
import ResizeObserver from "resize-observer-polyfill";
export default function App() {
const parentRef = useRef(null);
const [width, setWidth] = useState(0);
useEffect(() => {
const ro = new ResizeObserver((entries) => {
entries.forEach((entry) => setWidth(entry.contentRect.width));
});
ro.observe(parentRef.current);
return () => ro.disconnect();
}, []);
return (
<div ref={parentRef}>
<div> {width} </div>
</div>
);
}
code in action: https://codesandbox.io/s/reverent-galileo-q7np5?file=/src/App.js
In your particular example you're just getting width of an element and passing it to another element.
If you're using latest react version you should checkout new ref's api (https://reactjs.org/docs/refs-and-the-dom.html)
And your example will look something like that
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
width: 0
};
this.parentRef = React.createRef();
}
componentDidMount() {
window.addEventListener("resize", this.onResize);
this.onResize();
}
componentWillUnmount() {
window.removeEventListener("resize", this.onResize);
}
onResize = () => {
this.setState({
width: this.getParentSize()
});
};
getParentSize() {
return (this.parentRef.current && this.parentRef.current.offsetWidth) || 0;
}
render() {
return (
<div id="parent" ref={this.parentRef}>
<canvas
id="child"
width={this.getParentSize()}
style={{ background: "red" }}
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
I am not able to dynamically AJAX loaded HTML format data(as mentioned below) in dangerouslySetInnerHTML(ie., it is setting in DOM as the same string and not compiling). Please note that I am using axios plugin for AJAX API calls. When I try with the same HTML data giving statically in dangerouslySetInnerHTML, it is working perfectly. I don't know what is going there.
[{id: 1, image: "assets/img/news-sm.png", content: "<p>சிறுவனே!</p>"}]
My React Component code is
import React, { Component, PropTypes } from 'react';
import List from 'material-ui/List/List';
import ListItem from 'material-ui/List/ListItem';
import Divider from 'material-ui/Divider';
import { Media } from 'react-bootstrap';
import { map } from 'lodash';
class NewsList extends Component {
constructor(props) {
super(props);
}
handleListClick(d){
console.log(d);
}
renderLoadingComponent() {
return <ListItem style={{textAlign: 'center'}}>Loading...</ListItem>;
}
renderNothingFoundElement() {
return <ListItem style={{textAlign: 'center'}}>Nothing found</ListItem>;
}
render() {
const { data, loading } = this.props;
return (
<List className="list">
{
map(data, (d, index) =>
React.Children.toArray([
<ListItem className="list-item">
<Media onClick={(ev) => this.handleListClick(d)} className="media-align">
<Media.Left align="middle">
<img width={75} height={56} src={d.image} alt="news-image"/>
</Media.Left>
<Media.Body>
{d.content && <p dangerouslySetInnerHTML={{ __html: d.content }}></p>}
</Media.Body>
</Media>
</ListItem>,
(index + 1) !== data.length ? <Divider /> : ''
])
)
}
{ loading ? this.renderLoadingComponent() : data.length === 0 ? this.renderNothingFoundElement() : '' }
</List>
)
}
}
NewsList.propTypes = {
data: React.PropTypes.array.isRequired
};
export default NewsList;
I am calling the above Component from another Component like this
import React, { Component, PropTypes } from 'react';
import NewsList from '../components/news-list';
class Home extends Component {
constructor(props) {
super(props);
this.state = { ajaxData: [], ajaxLoading: false }
}
componentDidMount() {
axios.get('/ajaxUrl')
.then(response => this.setState({ajaxData: response.data}))
}
render() {
return (
<NewsList data={this.state.ajaxData} loading={this.state.ajaxLoading} />
)
}
}
Can someone please help me to solve this issue? Thanks.
Probably you have to specify The Component Lifecycle shouldComponentUpdate or forceUpdate or componentWillReceiveProps facebook.github.io
But you can also use a huck and set state in your Parent component to '' and on a callback set the NewList and pass props
this.setState({newList: ''},()=> this.setState({ newList: <NewList {...props}/>}))
Update The simple one
class Home extends Component {
constructor(props) {
super(props);
this.state = { ajaxData: [], ajaxLoading: false, newsList:'' }
}
componentDidMount() {
axios.get('/ajaxUrl')
.then(response => this.setState({ajaxData: response.data},
()=> this.setState({newsList: <NewsList data={this.state.ajaxData} loading={this.state.ajaxLoading} />})))
}
render() {
return (
<div> {this.state.newsList} <div/>
)
}}
Also you can implement method for a life cycle
shouldComponentUpdate(nextProps, nextState) {
return this.state.ajaxData != nextState.ajaxData;
}
I am starting to learn react and download and follow any tutorials in internet. I am trying to build friend list.
I have tree components,
friends_container:
import React from 'react';
import AddFriend from './add_friend.jsx'
import ShowList from './show_list.jsx'
class FriendsContainer extends React.Component {
constructor() {
this.state = {
friends: ['Jake Lingwall', 'Murphy Randall', 'Merrick Christensen']
}
}
addFriend(friend) {
this.setState({
friends: this.state.friends.concat([friend])
});
}
render() {
return (
<div>
<h3> Add your friend to friendslist </h3>
<AddFriend addNew={this.addFriend}/>
<ShowList names={this.state.friends}/>
</div>
)
}
}
export default FriendsContainer;
add_friend:
import React from 'react';
class AddFriend extends React.Component {
constructor() {
this.state = {newFriend: ''};
}
updateNewFriend(e) {
this.setState({
newFriend: e.target.value
})
}
handleAddNew() {
this.props.addNew(this.state.newFriend);
this.setState({
newFriend: ''
});
}
render() {
return (
<div>
<input type="text" value={this.state.newFriend} onChange={this.updateNewFriend}/>
<button onClick={this.handleAddNew}>Add Friend</button>
</div>
)
}
}
AddFriend.propTypes = { addNew: React.PropTypes.func.isRequired };
export default AddFriend;
show_list:
import React from 'react';
class ShowList extends React.Component {
render() {
var listItems = this.props.names.map((f, i) => <li key={i}>{f}</li>);
return (
<div>
<h3>Friends</h3>
<ul>
{listItems}
</ul>
</div>
)
}
}
ShowList.defaultProps = { names: [] };
export default ShowList;
and app.jsx
import React from 'react';
import FriendsContainer from './components/friends_container.jsx';
window.React = React;
React.render(<FriendsContainer />, document.body);
as you can see on the code, I am using es6 and babel as transcompiler.
My problem, I can not type any letters into the input field to add new friend into friends list. What am I doing wrong?
In the context of your updateNewFriend method, this refers to the window object and not the current component instance. You need to bind the method before passing it as the onChange event handler. See Function::bind.
You have two options:
class AddFriend extends React.Component {
constructor() {
// ...
this.updateNewFriend = this.updateNewFriend.bind(this);
this.handleAddNew = this.handleAddNew.bind(this);
}
}
or
class AddFriend extends React.Component {
render() {
return (
<div>
<input type="text" value={this.state.newFriend} onChange={this.updateNewFriend.bind(this)}/>
<button onClick={this.handleAddNew.bind(this)}>Add Friend</button>
</div>
)
}
}
Keep in mind that Function::bind returns a new function, so binding in render creates a function every time your component is rendered, though the performance impact is negligible.