1

I am trying to render a list of components in order with react, the component is updating this array of elements but is not re-ordering them.

Pseudo code;

class Form extends Component {
  //
  // .... other initialization code and logic
  //

  updatePositions() {
    //
    // re-order this.state.page.page_contents
    //
    this.setState({ page: this.state.page });
  }

  renderContents() {
    return this.state.page.page_content.map((c, i) => {
      return (<ContentItem
        key={ i }
        content={ c }
      />);
    });
  }

  render() {
    return (
      <div className="row">
        <div className="medium-12 columns">
          { this.renderContents() }
        </div>
      </div>
    );
  }
}

If i log out the results of page.page_content the items are being reordered in the array, however the form render is not re-rendering the contents in its new order

5
  • No id set for these items. Commented May 29, 2019 at 12:10
  • 1
    Are you make a workable example for us to have a look at? As we don't know the structure of everything or your code for re-ordering. Commented May 29, 2019 at 12:10
  • 2
    This is not a good approach this.setState({ page: this.state.page }) it implies you're mutating the state, if you want to change the state to a derived version of the previous state use: this.setState(prevState => ....). Commented May 29, 2019 at 12:11
  • @Titus What would i do inside that call back? Is this where i would set the new array contents? Commented May 29, 2019 at 12:13
  • 1
    Yes, what you return from the callback will be set as the new state, you could do something like this: this.setState(prevState => ({page: [...prevState.page].sort(.....)})) Commented May 29, 2019 at 12:14

4 Answers 4

1

You shouldn't be using array indices as keys if your array order is subject to change. Keys should be permanent, because React uses the keys to identify the components, and if a component receives a key that previously belonged to a different component, React thinks it is the same component as before.

Create unique keys for your elements that are permanent to those elements.

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

Comments

0

you could try to force update

 renderContents() {
          this.forceUpdate();           
          return this.state.page.page_content.map((c, i) => {
              return (<ContentItem
                key={ i }
                content={ c }
              />);
            });

          }

Comments

0

Don't mutate this.state, directly. Bring it into a new variable and then add it back into state.

Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.

from: https://reactjs.org/docs/react-component.html

Instead, you should try:

  updatePositions() {
    const page_contents = [...this.state.page.page_contents]
    // re order page_contents
    this.setState({ page: { page_contents });
  }

Comments

0
renderContents() {
    return this.state.page.page_content.map((c, i) => {
      return (<ContentItem
        key={ i }
        content={ c }
      />);
    });
  }

it's your code here - key={i} i is not changing so it will not re-render the component - if you want to re-render the component - please make sure that - key should change.

renderContents() {
    return this.state.page.page_content.map(c => {
      return (<ContentItem
        key={ c }
        content={ c }
      />);
    });
  }

c is content - if it's change then Component will re-render

this.setState({ page: this.state.page }) it's wrong - ur trying to set the same value in same variable again .

class Form extends Component {
  //
  // .... other initialization code and logic
  //

  updatePositions() {
    //
    // re-order this.state.page.page_contents
    //
    this.setState({ page: newValueFromAPI.page });
  }

  render() {
    const { page: { page_content } } = this.state
    return (
      <div className="row">
        <div className="medium-12 columns">
          { page_content.length > 0 && (
             page_content.map(c => <ContentItem key={c} content={c}/>)
          )}
        </div>
      </div>
    );
  }
}

2 Comments

c={ c } does not work as it returns [object Object] for all content items, however c={ JSON.stringify(c) } does work. Not a nice hack but it does solve the problem
JSON.stringify(c) will do the work but if value didn't changed then why to re-render it something should be changed - if you have a value in object which keep on changing then keep that as KEY - it will re-render the component when it's changed

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.