0

I have a function that renders content to page based on a state populated by API data, but I need to have an onClick event to refine that content;

So currently getPosts returns information from the state 'posts' which is provided with data from our API, but i want to filter this content further, so my idea is to have some sort of event listener, and if actioned, change the data coming out of getPosts.

constructor() {
    super();
    this.state = {
        posts: ""
    }
    this.getPosts = this.getPosts.bind(this);
}
async componentWillMount(){
    var data = await api.posts();
    this.setState({posts: data.data});
    console.log(this.state.posts);
}
getPosts(type){
        if(this.state.posts.length){
            return this.state.posts.map((content,index) => {
                var url = content.Title.replace(/[^\w\s]/gi, '');
                url = url.replace(/\s+/g, '-').toLowerCase();
                if(type === content.PostType){
                    //output something different
                }
                else{
                    return(
                        <Col md={4} className="mb-4" key={index}>
                            {content.title}
                        </Col>
                    );
                }
            })

        }
    }
    render() {
        return (
            <div>
            <p><button onClick={()=>{this.getPosts('blog')}}>blog</button> <button onClick={()=>{this.getPosts('news')}}>news</button></p>
            {this.getPosts()}
            </div>
        )
    }

So my getPosts works fine without any type, how to do tell it to re-output the function on the page based in the onClick event?

2
  • Sorry I don't understand well, you want to requesting data from api any time you click the button ? Commented Sep 27, 2019 at 16:08
  • No, i already have all the data in 'posts' - i was just explaining where the data was coming from. I just want to put a condition around the return on getPosts to filter this return down Commented Sep 27, 2019 at 16:17

2 Answers 2

1

Without getting into the complexities of context and keys, a component requires a change in props or state to re-render. To read more about state and component life-cycle, the docs have a great explanation for class components.

Your component does not re-render after the onClick event handler's call to getPosts because getPosts does not update internal component state. getPosts works within render because those values are being returned to React. By using getPosts as an onClick event handler, you are creating React elements and trying to return them to the window.

What follows should be treated as psuedo code that shows how to trigger your component to render different posts:

  • Consider adding another key to state in your constructor,
constructor(props) {
  super(props);
  this.state = {
    posts: "",
    type: null
  };
  this.getPosts = this.getPosts.bind(this);
  this.onClick = this.onClick.bind(this);
}
  • and creating a click handler that doesn't try to return React elements
function onClick(evt) {
  this.setState({ type: evt.target.value });
}
  • and values to your buttons
<button onClick={this.onClick} type="button" value="blog">blog</button>
  • Now your button will update state with your new post type, causing your component to re-render:
render() {
  return (
    <div>
      <p>
        <button onClick={this.onClick} type="button" value="blog">blog</button>
        <button onClick={this.onClick} type="button" value="news">news</button>
      </p>
      {this.getPosts()}
    </div>
  );
}

With the content type being stored in state, you can now implement your getPosts call in any way that works for you. Good luck!

It strays from the question asked, but it is worth noting componentWillMount is being deprecated, and componentDidMount is a preferable life-cycle function for side-effects and asynchronous behavior. Thankfully, the documentation has lots of details!

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @BEVR1337 - that's exactly what i was looking for! Just a note; I couldn't get your code to pick up the target.value, not sure if perhaps theres a syntax error there, so i just past the val into the param 'onClick={() => { this.onClick("blog") }}'. I also noticed that you have to pass it through on the onClick as a arrow function or it runs the function onload (which i found the answer to here: stackoverflow.com/questions/33846682/…)
@grhmstwrt Glad this was helpful! Happy coding! evt.target.value will be null unless your html element has the attribute value, e.g. <button value="foobar">click me!</button>. Was that a possible issue? In my example the function should not run onload, so I would need to see more code to understand how you reproduced that error, likely by calling a function instead of passing the function itself?
0

Ok so you should start by changing your default this.state to

this.state = {
  posts: []
}

remember that you want to iterate over an array of data instead of iterate a string, that will throw an error if you do that, so better keep from the beginning the data type you want to use.

Then you need to separate the responsibility for your getPosts method, maybe getPostByType is a better name for that, so you have

getPostByType(type) {
  // if type is same as content.PostType then return it;
  const nextPosts = this.state.posts.filter((content) => type === content.PostType);
  this.setState({ posts: nextPosts });
}

and finally you can iterate over posts, like this

render() {
  // better use content.id if exists instead of index, this is for avoid problems 
  // with rerender when same index key is applied.
  const posts = this.state.posts.map((content, index) => (
    <Col md={4} className="mb-4" key={content.id || index}>
      {content.title}
    </Col>
  ));
  return (
    <div>
      <button onClick={() => this.getPostByType('blog')}>Show Blog Posts</button>
      {posts}
    </div>
  );
}

Then you can use getPostsByType any time in any click event passing the type that you want to render.

3 Comments

this errors out: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
please remember not to use getPostByType directly into the render method only invoke the function in onClick events or any other events.
please can you provide the render method code here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.